化:Python批量生成大量小文件的實踐與避坑指南)
I got a bad idea.. 這種念頭估計每個開發(fā)者在寫代碼或準備測試時都冒出來過。很多時候我們會下意識地壓制它覺得正規(guī)方案應(yīng)該更專業(yè)、更穩(wěn)妥。但在我最近的實踐里恰恰是這個看起來不怎么樣的壞主意幫我用最短路徑驗證了一類性能場景的邊界。這篇文章想借一個典型場景展開使用 Python 快速生成大量小文件。這個需求聽起來簡單背后卻藏著一堆容易被忽略的細節(jié)比如文件句柄、磁盤 IO、并發(fā)模型和目錄結(jié)構(gòu)。我將從第一版“肉眼可見不優(yōu)雅”的腳本開始逐步分析它的性能表現(xiàn)和坑點然后給出更合理的優(yōu)化方向。如果你也經(jīng)常要準備測試數(shù)據(jù)或者需要驗證文件遷移、備份工具在小文件場景下的表現(xiàn)這篇內(nèi)容會比較適合你。1. 背景與核心概念1.1 開發(fā)語境下的“壞主意”是什么這里的 bad idea并不是指邏輯錯誤、安全漏洞或明顯不可行的方案而是指那些“看起來不夠優(yōu)雅、不夠標準化甚至有點笨”的臨時思路。比如測試數(shù)據(jù)不夠直接寫一個循環(huán)生成 2 萬個臨時文件。想驗證接口性能不求助于壓測工具而是用腳本慢速發(fā)請求。臨時排查線上問題不去搭監(jiān)控而是打印一堆日志再分析。這類思路之所以被稱為“壞主意”是因為它們在工程化視角下有很多問題沒有復(fù)用性、缺少參數(shù)校驗、性能不一定好、不夠安全。但換一個角度看它們又是低成本探索的代名詞。很多復(fù)雜問題恰恰是在這種“先跑起來再說”的過程中暴露出來的。1.2 場景還原為什么要一次生成大量小文件文件系統(tǒng)層面的小文件性能問題在網(wǎng)絡(luò)存儲、對象存儲、日志歸檔、數(shù)據(jù)遷移等場景中非常常見。例如測試對象存儲工具在海量小文件場景下的上傳耗時。驗證備份腳本對幾十萬個文本文件的掃描效率。模擬一段業(yè)務(wù)日志目錄供日志采集 Agent 消費。評估文件同步工具在文件數(shù)量巨大時的分片策略。在這些場景里你需要先“造一批數(shù)據(jù)”。手動復(fù)制文件太慢系統(tǒng)命令在某些環(huán)境里又不一定可用于是最直接的想法就是寫一個 Python 腳本循環(huán) open、write、close。這個方案聽起來像典型的“壞主意”因為它沒有考慮性能也沒有考慮文件系統(tǒng)限制。但它足夠直觀也足夠快速。1.3 為什么要保留壞主意我并不是鼓勵把所有不規(guī)范的思路直接搬到生產(chǎn)環(huán)境而是建議在方案評估初期保留一個“驗證窗口”。理由是成本低一個最小的循環(huán)腳本可能只需要十幾行代碼跑一次就能得到真實數(shù)據(jù)。能暴露邊界文件數(shù)量的上升會帶來什么問題只有真實測試才會告訴你。幫助你建立性能直覺紙上談兵猜不出 10000 個小文件要寫多久但跑一遍就有了感性認識。為正式方案提供對照基線后續(xù)無論使用并發(fā)、分目錄還是換存儲都可以和最初的“壞主意”版本做對比。因此本文會完整演示這條路徑寫一個看起來很不專業(yè)的腳本看它跑出什么結(jié)果再分析慢在哪里最后找到改進方向。2. 環(huán)境準備與實驗?zāi)繕?.1 運行環(huán)境說明本文示例以 Python 腳本為主使用標準庫os、time、concurrent.futures不需要安裝第三方依賴。示例環(huán)境為 Python 3.10 以上版本W(wǎng)indows、Linux、macOS 均可運行但不同操作系統(tǒng)的文件系統(tǒng)行為會略有差異。需要說明的是不同磁盤類型對結(jié)果影響非常大機械硬盤寫入大量小文件時隨機尋道開銷明顯SSD 相對快很多而 Linux 的 tmpfs 或 Windows 虛擬內(nèi)存盤則完全在內(nèi)存中運行速度最快。因此下面給出的耗時數(shù)據(jù)只作為相對對比參考你需要在你的實際環(huán)境中重新跑一遍。2.2 實驗?zāi)繕宋覀兿M卮鹣旅鎺讉€問題用最樸素的 for 循環(huán)寫 10000 個小文件到底需要多久換成線程池并發(fā)性能是否一定提升換成進程池會不會更快不同方案在文件數(shù)量增大后各自會遇到什么問題從工程角度最優(yōu)方案是不是只剩“減少文件數(shù)量”一條路帶著這些問題我們開始動手。2.3 項目結(jié)構(gòu)與產(chǎn)物為了保持實驗清晰我建議把腳本分別保存為獨立文件每次運行生成獨立的臨時目錄。整體結(jié)構(gòu)如下bad_idea_lab/ ├── file_writer_v1.py # 第一版同步循環(huán)寫文件 ├── file_writer_v2.py # 第二版線程池并發(fā)寫文件 ├── file_writer_v3.py # 第三版進程池并發(fā)寫文件 ├── benchmark.py # 簡單對比腳本 └── tmp_bad_idea_v1/ # 運行后自動生成用完可刪除這樣做的好處是每個版本獨立可運行方便對比耗時也方便清理垃圾文件。3. 第一版壞主意同步循環(huán)寫文件3.1 思路分析第一版方案最簡單通過os.makedirs建立目錄然后使用 for 循環(huán)逐個創(chuàng)建文件每個文件只寫入幾行文本。這是一個非常經(jīng)典的“壞主意”模板因為它完全沒有考慮性能也沒有處理可能出現(xiàn)的句柄和磁盤占用問題。但正是這種粗暴方案能最直觀地反映文件系統(tǒng)處理小文件時的基礎(chǔ)成本。3.2 完整代碼# 文件路徑file_writer_v1.py import os import time def create_dir(target: str) - None: 創(chuàng)建目標目錄已存在時忽略錯誤。 os.makedirs(target, exist_okTrue) def write_v1(target_dir: str, file_count: int 10000) - None: 第一版同步循環(huán)寫入 file_count 個小文件。 create_dir(target_dir) start time.perf_counter() for i in range(file_count): file_path os.path.join(target_dir, fv1_{i:06d}.txt) with open(file_path, w, encodingutf-8) as f: f.write(ffile index: {i}, hello bad idea\n) elapsed time.perf_counter() - start print(fv1 同步寫入 {file_count} 個文件總耗時 {elapsed:.3f} 秒) if __name__ __main__: write_v1(./tmp_bad_idea_v1, file_count10000)這段代碼有幾個細節(jié)值得解釋time.perf_counter()用于精確計時比time.time()更適合短時間測量。os.makedirs(..., exist_okTrue)避免手動判斷目錄是否存在。with open(...)保證文件正常關(guān)閉這是最低限度的規(guī)范。文件名使用:06d格式化保證排序時不會出現(xiàn)v1_10排在v1_9前面的情況。3.3 運行方式在終端中執(zhí)行python file_writer_v1.py運行結(jié)束后當前目錄會出現(xiàn)tmp_bad_idea_v1目錄里面包含 10000 個 txt 小文件。腳本會輸出總耗時。3.4 預(yù)期結(jié)果與現(xiàn)象分析以我的測試環(huán)境為例單次運行大概會輸出v1 同步寫入 10000 個文件總耗時 18.662 秒注意這個數(shù)字僅供參考。在機械硬盤上可能超過 1 分鐘在高端 SSD 上可能只要幾秒。真正值得關(guān)注的是10000 個文件本身并不算多但同步寫入時間已經(jīng)明顯超出預(yù)期。這說明大量小文件的寫入瓶頸通常不在于文件內(nèi)容本身的大小而在于文件系統(tǒng)需要為每個文件分配 inode、維護目錄項、更新元數(shù)據(jù)。這些操作在機械硬盤上會帶來隨機尋址開銷在 SSD 上雖然好一些但依然是有成本的。所以第一版腳本第一次跑完我們就已經(jīng)得到了一個關(guān)鍵結(jié)論“批量創(chuàng)建小文件”絕不是普通循環(huán)能高效完成的任務(wù)。4. 數(shù)據(jù)異常到底慢在哪4.1 從現(xiàn)象看規(guī)律如果繼續(xù)增加文件數(shù)量比如從 10000 增加到 50000會發(fā)現(xiàn)耗時并不是線性增長而有可能是接近線性甚至超線性增長。原因在于單目錄下的“目錄項”文件會越來越大。文件系統(tǒng)在查詢和插入目錄項時需要維護索引結(jié)構(gòu)。磁盤剩余空間減少后分配數(shù)據(jù)塊時可能變得更復(fù)雜。這也是為什么很多文件遷移工具強調(diào)“按時間或前綴分目錄存儲”而不是把所有文件堆在同一個目錄下。4.2 文件系統(tǒng)層面的成本拆解一個小文件從創(chuàng)建到寫入完成大概要經(jīng)歷以下步驟在目錄中檢查文件名是否存在。分配 inode。在目錄結(jié)構(gòu)中插入新的目錄項。分配數(shù)據(jù)塊。將用戶態(tài)數(shù)據(jù)復(fù)制到內(nèi)核頁緩存。關(guān)閉文件時可能需要刷新元數(shù)據(jù)到磁盤。其中第 3 步和第 4 步在大批量小文件場景下最容易成為瓶頸。尤其當目錄中文件數(shù)量達到上萬甚至十萬級別目錄項相關(guān)的查找和維護成本會迅速上升。4.3 為什么不能只憑“慢”來下結(jié)論第一版腳本跑得慢不代表 Python 語言慢也不代表循環(huán)寫法差。為了找出真正原因我們需要控制變量。比較直觀的做法是同樣寫 10000 個文件但提前生成好內(nèi)容只測 open/write/close 的開銷。改成把 10000 行內(nèi)容寫入同一個文件對比總耗時。分別測試空文件和小文件中寫入若干字節(jié)的差異。通過這些對照你會發(fā)現(xiàn)單文件寫入本身非??煺嬲拈_銷在于每個獨立文件帶來的系統(tǒng)調(diào)用和元數(shù)據(jù)操作。這也是后續(xù)所有優(yōu)化方向的出發(fā)點。5. 壞主意升級并發(fā)寫文件對比5.1 線程池版本既然同步循環(huán)慢很多人會立刻想到“用多線程提速”。這個思路在文件 IO 場景下有一定道理因為文件讀寫時線程經(jīng)常處于等待狀態(tài)Python 的 GIL 通常會在 IO 等待時釋放所以并發(fā)線程可以重疊一部分等待時間。下面是一個線程池版本# 文件路徑file_writer_v2.py import os import time from concurrent.futures import ThreadPoolExecutor, as_completed def write_one_file(file_path: str, content: str) - None: 寫入單個文件。 with open(file_path, w, encodingutf-8) as f: f.write(content) def write_v2(target_dir: str, file_count: int 10000, workers: int 8) - None: 線程池并發(fā)寫入 file_count 個小文件。 os.makedirs(target_dir, exist_okTrue) content hello bad idea with thread pool\n start time.perf_counter() with ThreadPoolExecutor(max_workersworkers) as executor: futures [ executor.submit( write_one_file, os.path.join(target_dir, fv2_{i:06d}.txt), content, ) for i in range(file_count) ] for future in as_completed(futures): future.result() elapsed time.perf_counter() - start print(fv2 線程池并發(fā)寫入 {file_count} 個文件總耗時 {elapsed:.3f} 秒) if __name__ __main__: write_v2(./tmp_bad_idea_v2, file_count10000, workers8)這里需要注意future.result()的調(diào)用。它一方面用于獲取結(jié)果另一方面也能讓底層異常被拋出。如果某個文件寫入失敗比如磁盤滿或權(quán)限不足腳本不會靜默失敗。5.2 進程池版本進程池相比線程池能夠利用多核 CPU但也會帶來進程創(chuàng)建和進程間通信的開銷。在單純寫文件場景下進程池不一定比線程池更優(yōu)但它是一個值得對比的參考# 文件路徑file_writer_v3.py import os import time from concurrent.futures import ProcessPoolExecutor, as_completed def write_one_file(file_path: str, content: str) - None: 寫入單個文件。 with open(file_path, w, encodingutf-8) as f: f.write(content) def write_v3(target_dir: str, file_count: int 10000, workers: int 4) - None: 進程池并發(fā)寫入 file_count 個小文件。 os.makedirs(target_dir, exist_okTrue) content hello bad idea with process pool\n start time.perf_counter() with ProcessPoolExecutor(max_workersworkers) as executor: futures [ executor.submit( write_one_file, os.path.join(target_dir, fv3_{i:06d}.txt), content, ) for i in range(file_count) ] for future in as_completed(futures): future.result() elapsed time.perf_counter() - start print(fv3 進程池并發(fā)寫入 {file_count} 個文件總耗時 {elapsed:.3f} 秒) if __name__ __main__: write_v3(./tmp_bad_idea_v3, file_count10000, workers4)5.3 對比結(jié)果與隱藏問題在我本地的 Linux SSD 環(huán)境上10000 個文件的大致表現(xiàn)是方案耗時量級說明同步單線程15 ~ 25 秒最差但最穩(wěn)線程池 8 并發(fā)5 ~ 10 秒提升明顯進程池 4 并發(fā)6 ~ 12 秒有一定提升但不如線程池顯著需要強調(diào)這個對比并不嚴謹。文件系統(tǒng)緩存、磁盤剩余空間、文件大小、并發(fā)數(shù)都會影響結(jié)果。但從趨勢上看并發(fā)確實能改善寫入耗時。但并發(fā)也帶來新的隱藏問題線程數(shù)過大時系統(tǒng)同時打開大量文件句柄可能觸及ulimit限制。并發(fā)度太高時磁盤尋道和系統(tǒng)調(diào)度開銷可能反而增加。如果文件名生成邏輯有問題可能出現(xiàn)覆蓋寫入導(dǎo)致實際寫入文件數(shù)少于預(yù)期。程序運行過程中一旦被中斷會留下一堆半成品文件下次清理成本很高。所以并發(fā)不是銀彈它只是把瓶頸從“串行等待”轉(zhuǎn)移到了“資源競爭”。5.4 用數(shù)據(jù)判斷不憑感覺為了讓對比更嚴謹可以寫一個簡單的benchmark.py把同步和線程池版本放到同一個進程里跑并分別計時# 文件路徑benchmark.py import os import time from concurrent.futures import ThreadPoolExecutor, as_completed def write_sync(target_dir: str, count: int) - float: os.makedirs(target_dir, exist_okTrue) start time.perf_counter() for i in range(count): with open(os.path.join(target_dir, fsync_{i:06d}.txt), w, encodingutf-8) as f: f.write(fbatch {i}\n) return time.perf_counter() - start def write_thread(target_dir: str, count: int, workers: int) - float: os.makedirs(target_dir, exist_okTrue) def _write(path: str) - None: with open(path, w, encodingutf-8) as f: f.write(batch\n) start time.perf_counter() with ThreadPoolExecutor(max_workersworkers) as executor: futures [ executor.submit(_write, os.path.join(target_dir, fthread_{i:06d}.txt)) for i in range(count) ] for fut in as_completed(futures): fut.result() return time.perf_counter() - start if __name__ __main__: n 5000 t1 write_sync(./bench_sync, n) t2 write_thread(./bench_thread, n, workers8) print(fsync : {t1:.3f}s) print(fthread: {t2:.3f}s)運行結(jié)果可能類似sync : 9.412s thread: 4.887s這再次說明在批量小文件場景中合理并發(fā)是有效的優(yōu)化方向之一。但到底選線程池還是進程池要結(jié)合實際環(huán)境來決定。6. 優(yōu)化思路從壞主意走向可行方案6.1 根本性優(yōu)化減少文件數(shù)量性能最好的“文件寫入方案”是不創(chuàng)建那么多文件。如果業(yè)務(wù)允許可以考慮把多個小文件合并成一個文件或者使用壓縮包、數(shù)據(jù)庫、隊列等方式替代。例如日志場景可以使用追加式日志文件而不是每天生成成千上萬個 txt。數(shù)據(jù)交換場景可以使用 JSON Lines 或 Parquet 文件將多條記錄放在一個文件內(nèi)。臨時測試數(shù)據(jù)可以直接用 tar 或其他打包工具生成。減少文件數(shù)量不僅能提升寫入速度還能降低后續(xù)備份、遷移、清理的成本。6.2 分目錄存儲如果業(yè)務(wù)確實需要獨立的小文件那么盡量避免把所有文件塞進同一個目錄??梢愿鶕?jù)時間、哈希值或業(yè)務(wù) ID 劃分子目錄。例如import os base_dir ./data for i in range(10000): sub_dir os.path.join(base_dir, fbatch_{i // 1000}) os.makedirs(sub_dir, exist_okTrue) file_path os.path.join(sub_dir, ffile_{i:06d}.txt) # 寫入 file_path每個目錄只放 1000 個文件目錄項規(guī)模小查找和維護開銷明顯降低。你還可以用首字母、日期等更合理的分片規(guī)則。6.3 使用更快的存儲介質(zhì)如果目標是驗證業(yè)務(wù)邏輯而不是測試磁盤性能可以把臨時數(shù)據(jù)放到 tmpfs 等內(nèi)存文件系統(tǒng)上。這樣文件寫入操作不會真正落到磁盤速度會大幅提升。Linux 下的示例mkdir /dev/shm/bad_idea_data python file_writer_v1.py /dev/shm/bad_idea_dataWindows 下可以臨時使用內(nèi)存盤工具或直接把數(shù)據(jù)放在 SSD 的臨時目錄中。需要提醒的是內(nèi)存文件系統(tǒng)的數(shù)據(jù)在系統(tǒng)重啟后會丟失不適合存放重要數(shù)據(jù)。6.4 控制并發(fā)打開的文件句柄數(shù)即使使用線程池也不要無腦設(shè)置幾百個線程。文件句柄是系統(tǒng)資源過高并發(fā)可能導(dǎo)致OSError: [Errno 24] Too many open files。可以通過信號量限制同時寫入的文件數(shù)import threading import os import time from concurrent.futures import ThreadPoolExecutor semaphore threading.Semaphore(32) def write_one_with_limit(file_path: str, content: str) - None: with semaphore: with open(file_path, w, encodingutf-8) as f: f.write(content) def write_v4(target_dir: str, file_count: int 10000, workers: int 8) - None: os.makedirs(target_dir, exist_okTrue) start time.perf_counter() content hello with limited concurrency\n with ThreadPoolExecutor(max_workersworkers) as executor: futures [ executor.submit( write_one_with_limit, os.path.join(target_dir, fv4_{i:06d}.txt), content, ) for i in range(file_count) ] for fut in futures: fut.result() print(fv4 并發(fā)受限寫入 {file_count} 個文件總耗時 {time.perf_counter() - start:.3f} 秒)信號量設(shè)置為 32表示同時最多允許 32 個文件處于打開狀態(tài)。這是一種兼顧并發(fā)和資源控制的思路。6.5 監(jiān)控與清理策略大量生成文件后一定要考慮清理策略。最穩(wěn)妥的方式是使用獨立臨時目錄并在腳本結(jié)束時按需清理import shutil shutil.rmtree(./tmp_bad_idea_v1, ignore_errorsTrue)如果你在運行過程中手動取消腳本殘留的臨時目錄可以用下面的命令快速清理rm -rf tmp_bad_idea_v1 tmp_bad_idea_v2 tmp_bad_idea_v3也可以在腳本開頭自動清理舊目錄避免下次運行時出現(xiàn)文件數(shù)疊加。7. 常見問題與排查思路7.1 高頻問題清單問題現(xiàn)象常見原因解決思路報錯OSError: [Errno 24] Too many open files并發(fā)打開文件句柄過多超過系統(tǒng)限制使用信號量限制并發(fā)數(shù)減少max_workers寫入完成后文件數(shù)少于預(yù)期文件名重復(fù)或任務(wù)被中斷使用唯一編號或uuid增加中途異常處理磁盤空間快速耗盡未預(yù)估單個文件大小和總文件數(shù)乘積先計算總大小設(shè)置磁盤占用上限單目錄文件過多導(dǎo)致操作卡頓目錄項維護成本升高按前綴或批次拆分子目錄線程池版本反而更慢并發(fā)數(shù)設(shè)置過高或磁盤性能有限降低并發(fā)數(shù)對比不同并發(fā)度腳本中斷后殘留大量垃圾文件未設(shè)計清理機制使用統(tǒng)一臨時目錄腳本退出時清理不同機器上耗時差異巨大磁盤類型、文件系統(tǒng)、緩存策略不同明確記錄環(huán)境信息再對比數(shù)據(jù)7.2 文件句柄耗盡的排查步驟如果遇到句柄耗盡可以按以下順序排查檢查當前系統(tǒng)打開文件限制。Linux 下執(zhí)行ulimit -n。檢查腳本是否所有文件都使用了with open(...)。如果用了手工open()卻忘記close()很容易泄漏句柄。檢查線程池或進程池是否清理完成是否需要顯式shutdown()。調(diào)大系統(tǒng)限制需要謹慎推薦優(yōu)先調(diào)整代碼并發(fā)邏輯。7.3 為什么線程池寫文件比同步快Python 的 GIL 對 CPU 密集型任務(wù)影響較大但文件讀寫涉及系統(tǒng)調(diào)用線程在等待 IO 時會釋放 GIL因此線程池能有效重疊多個文件的等待時間。如果你的磁盤和操作系統(tǒng)能支持并發(fā) IO線程池通常就能帶來明顯提升。7.4 為什么進程池沒有想象中快進程池雖然能利用多核但每個進程都需要獨立初始化 Python 解釋器進程間任務(wù)分發(fā)也有通信成本。對于簡單的小文件寫入這些開銷可能抵消并發(fā)收益。更重要的是磁盤 IO 的瓶頸往往不在 CPU而在于設(shè)備的響應(yīng)能力。多進程不能憑空提高磁盤吞吐。8. 工程實踐如何對待開發(fā)中的壞主意8.1 先用最小原型驗證當你冒出“壞主意”時不必急著否定它。先問自己實現(xiàn)這個原型需要多久如果是十分鐘之內(nèi)完全可以寫出來跑一遍。原型的作用不是成為最終方案而是幫你盡早看到真實數(shù)據(jù)和潛在問題。本文中的 v1 腳本就是典型的最小原型。它沒有參數(shù)校驗沒有優(yōu)雅的錯誤處理甚至沒有考慮文件命名沖突。但它只用了不到二十行代碼就讓我們獲得了第一手性能數(shù)據(jù)。8.2 用指標代替直覺討論方案好壞時不要總說“感覺這樣更快”“這樣更穩(wěn)”。建議記錄以下幾個維度的指標總耗時。文件數(shù)量。單個文件大小。磁盤空間占用。系統(tǒng)打開文件數(shù)量。并發(fā)線程數(shù)或進程數(shù)。這些指標構(gòu)成了一個可復(fù)現(xiàn)的實驗環(huán)境。后續(xù)優(yōu)化時你只需要改變一個變量就能判斷它是否有效。8.3 在隔離環(huán)境中實驗“壞主意”之所以危險是因為一旦直接在正式目錄或生產(chǎn)環(huán)境運行后果可能不可控。比如用腳本在線上業(yè)務(wù)目錄里生成海量文件會快速消耗磁盤空間甚至影響業(yè)務(wù)讀寫。正確的做法是使用獨立的臨時目錄。估算總文件大小并預(yù)留充足空間。在測試環(huán)境或容器中運行。確保腳本可以隨時中止并設(shè)計清理方案。8.4 把壞主意沉淀為文檔或 issue很多團隊只記錄最終方案不記錄被淘汰的“壞主意”。這其實是一種浪費。一次實驗得到的性能數(shù)據(jù)、排錯過程、失敗原因可能比最終上線的那幾行代碼更有價值。建議在項目的docs目錄或 issue 中簡單記錄當時為什么提出這個方案。原型如何實現(xiàn)。測試數(shù)據(jù)是什么。為什么最終沒有采用或如何優(yōu)化。對后續(xù)項目的借鑒意義。這些“壞主意記錄”往往能幫助團隊避免重復(fù)踩坑。8.5 從壞主意中提煉規(guī)范壞主意實驗做完后不要止步于“原來這樣不行”。進一步思考什么樣的情況下這個方案是可行的什么情況下必須避免回到本文的場景可以提煉出以下經(jīng)驗如果只是臨時造幾十個文件循環(huán)寫沒問題。如果文件數(shù)達到幾千甚至數(shù)萬優(yōu)先考慮分目錄和并發(fā)。如果文件數(shù)達到十萬以上單機腳本可能已經(jīng)不適合需要借助專門工具或分布式方案。無論哪種場景都要設(shè)計好文件清理機制。這些經(jīng)驗比“不要使用壞主意”更有操作價值。9. 總結(jié)回到最初的標題I got a bad idea。在實際開發(fā)中我們真的不需要害怕這種想法。真正需要注意的是“如何處理壞主意”——是直接壓下去還是給它一個低成本驗證窗口再從驗證結(jié)果里提煉出有效信息。本文通過“用 Python 批量生成大量小文件”這個例子完整走了一遍從同步循環(huán)到并發(fā)升級再到分目錄、限流和清理策略的路徑。過程中我們看到了性能瓶頸來自文件系統(tǒng)元數(shù)據(jù)操作也看到了并發(fā)不是銀彈還總結(jié)了文件句柄、磁盤占用、單目錄文件過多等常見問題的排查思路。如果你下次也冒出一個類似的“壞主意”不妨按這個方式處理寫一個最小原型在隔離環(huán)境跑一次記錄下真實數(shù)據(jù)再決定是優(yōu)化還是放棄。無論結(jié)論如何你都會比“只在腦子里想”獲得更多確定的東西。