資料夾狂塞 750 萬個檔案的案例中,之所以東窗事發是因為清查磁碟空間發現帳面的檔案大小總和跟剩餘空間對不起來,最後追出一個擁有 750 萬個小檔案(958 Bytes)的資料夾,檔案大小合計 7G,但實際用掉 30G,理由是 NTFS 配置空間會以 4KB 為單位,若資料少於 4KB,剩下的空間無法給其他檔案用,就會浪費掉。這種設計是基於效率的取捨,在正常情況下一般是無感的,惟有遇上 750 萬這種變態數字,750 萬 x (4KB - 958 Bytes),差異高達 23GB,再遇上系統碟只有 80GB 格外明顯,才會因為有近三成空間消失被爆出來。

為了重現巨量小檔會因 4KB 配置單位產生空間浪費,我隨手寫了一小段 PowerShell 在資料夾建立 200 萬個小檔案(每個不到 0.5KB),想重現浪費 0.5G 檔案吃掉 8GB 空間的結果:

等了好一陣子才建完,200 萬 x 0.5KB 檔案總計不到 500M,而佔用磁碟空間為 200 萬 x 4KB 估計為 8GB。高高興興想從檔案總管資料夾屬性驗證結果... 登楞!!

沒看到預期中的 8GB,卻看到磁碟大小(Size on Disk)為 0 的神奇結果!!

經過研究,才知道 NTFS 沒那麼笨,傻到檔案只有一個 Byte 也撥 4KB 給它,當遇到資料很少的小檔案,NTFS 會改用更有效率的方式儲存。

背後原理有點小複雜,有興趣的同學可以參考這篇微軟部落格文章 - The Four Stages of NTFS File Growth。簡單來說,當我們在 NTFS 建立檔案,作業系統會在 MFT (Master File Table) 為它新增一筆大小固定為 1KB 記錄,這 1KB 將包含檔名、檔案時間、唯讀/隱藏旗標等屬性資訊,還有檔案內容相關資訊。一般檔案的檔案內容相關資訊多為 Mapping Pair 指向實際儲存內容的空間(就是前面說到以 4KB 為配置單位的區塊空間):

但如果檔案內容夠小,小到可以塞進檔案記錄 1KB 的剩餘空間,NTFS 便會將檔案內容直接存進記錄裡,不另外配置 4KB 空間。這便是為什麼 200 萬個 484 Bytes 小檔完全沒佔用儲存空間的原因 - 資料寄生在檔案記錄裡。

學會這點,小改一下程式,把資料量提高到 1024 Bytes。

成功重現問題,檔案總計約 2GB,佔用磁碟空間約 8GB。(註:精準的 1K 應該是 1024,所以1MB = 1024*1024 Bytes,1GB = 1024*1024*1024 Bytes,故 200 萬個 1KB 檔案會少於 2GB,此處忽略這部分誤差)

另外還有一種情況會造成檔案大小與佔用磁碟大小不一致,NTFS 下的檔案有所謂的 Alternative Data Stream,可用來存入額外資料,以下做個簡單示範,我在 1024 Bytes 大小的 data.txt 加名為 hidden 的 Alternative Data Stream 寫入 1024 Bytes,則這個檔案帳面資料大小為 1024 Bytes,但實際佔用磁碟大小為 4KB + 4KB = 8KB:

我們可以用 dir /r 跟 more 檢查與讀取:參考

注意,ADS 是 NTFS 專屬規格,當複製到其他檔案系統 (如 FAT32, exFAT) 時會遺失,Windows 有時會彈出如下的警示:

了解以上原則,下回再遇到檔案大小與實際磁碟大小有差異,就不會再一頭霧水了。

Demostrating how NTFS save data in file record to save space for small files.


Comments

# by Huang

windows用的1KB=1024 Bytes其實反而才是正確的,是基於電腦高低電位的2進度而形成,也是資訊相關科系學習的基礎內容。 但這種知識也造成很多認定的困擾。 (主計人員會說,文件寫磁碟空間1000MB怎麼電腦畫面上少於1GBytes,還要解釋半天XD) 因此市面上常用的1KB並不是1024 Bytes而是1000 Bytes,可能基於大多非資訊背景人士的方便溝通。不過還是一直存在認定的困擾(學電腦的人會說,硬碟標示1GB但不等於1024MB,偷工減料吧)。 基於這種困擾,演化出MB與MiB...等。 (目前不太準確的標示法反而變主流)XD

# by Jeffrey

to Huang, 原來 1024 還演化出 MB 跟 MiB 兩種單位,長知識了! 謝謝分享。

# by Andy Kwok

To Huang, 網絡工程人員也是以1 Kb = 1000 b 的基礎計算,加上網絡傳輸速度以 bits per second 計算,所以與其他IT人員溝通時,經常要做換算。

Post a comment