不知道你有沒有遇到過當一個檔案夾下檔案特別多,在下面執行ls命令的時候要等好長時間才能展現出來的問題?如果有,你有想過這是為什么嗎,我們該如何解決?
要想深入理解這個的問題產生的原因,我們就需要從檔案夾占用的磁盤空間開始討論了,
inode消耗驗證
在《新建一個空檔案占用多少磁盤空間?》中我提到了每一個檔案會消耗其所在檔案夾中的一點空間,檔案夾呢,其實也一樣會消耗inode的, 我們先看一下當前inode的占用情況
# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
......
/dev/sdb1 2147361984 12785020 2134576964 1% /search
再創建一個空檔案夾
# mkdir temp
# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
......
/dev/sdb1 2147361984 12785021 2134576963 1% /search
通過IUsed可以看到,和空檔案一樣,空的檔案夾也會消耗掉一個inode,不過這個很小,我的機器上才是256位元組而已,應當不是造成ls命令卡主的元兇,
block消耗驗證
檔案夾的名字存在哪兒了呢?嗯,和《新建一個空檔案占用多少磁盤空間?》里的檔案類似,會消耗一個ext4_dir_entry_2 (今天用ext4舉例,它在linux原始碼的fs/ext4/ex4.h檔案里定義),放到其父目錄的block里了,根據這個,相信你也很快能想到,如果它自己節點下創建一堆檔案的話,就會占用它自己的block,我們來動手驗證一下:
# mkdir test
# cd test
# du -h
4.0K .
這里的4KB就表示消耗掉了一個block, 空檔案不消耗block,空目錄為啥一開始就消耗block了呢,那是因為其必須默認帶兩個目錄項"."和"..",另外這個4K在你的機器上不一定是這么大,它其實是一個block size,在你格式化的時候決定的,
我們再新建兩個空的檔案,再查看一下:
# touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
# touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# du -h
4.0K .
貌似,沒有什么變化,這是因為
- 第一、新的空檔案不占用block,所以這里顯示的仍然是目錄占用的block,
- 第二、之前檔案夾創建時候分配的4KB里面空閑空間還有,夠放的下這兩個檔案項
那么我再多創建一些試試,動用腳本創建100個檔案名長度為32Byte的空檔案,
#!/bin/bash
for((i=1;i<=100;i++));
do
file="tempDir/"$(echo $i|awk '{printf("%032d",$0)}')
echo $file
touch $file
done
# du -h
12K .
哈哈,這時我們發現目錄占用的磁盤空間變大了,成了3個Block了,當我們創建到10000個檔案的時候,
# du -h
548K .
在每一個ext4_dir_entry_2里都除了檔案名以外,還記錄著inode號等資訊,詳細定義如下:
struct ext4_dir_entry_2 {
__le32 inode; /* Inode number */
__le16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */
__u8 file_type;
char name[EXT4_NAME_LEN]; /* File name */
};
我們計算一下,平均每個檔案占用的空間=548K/10000=54位元組,也就是說,比我們的檔案名32位元組大一點點,基本對上了, 這里我們也領會到一個事實,檔案名越長,在其父目錄中消耗的空間也會越大,
本文結論
一個檔案夾當然也是要消耗磁盤空間的,
- 首先要消耗掉一個inode,我的機器上它是256位元組
- 需要消耗其父目錄下的一個目錄項
ext4_dir_entry_2,保存自己inode號,目錄名, - 其下面如果創建檔案夾或者檔案的話,它就需要在自己的block里
ext4_dir_entry_2陣列
目錄下的檔案/子目錄越多,目錄就需要申請越多的block,另外ext4_dir_entry_2大小不是固定的,檔案名/子目錄名越長,單個目錄項消耗的空間也就越大,
對于開篇的問題,我想你現在應該明白為什么了,問題出在檔案夾的block身上,
這就是當你的檔案夾下面檔案特別多,尤其是檔案名也比較長的時候,它會消耗掉非常多的block,當你遍歷檔案夾的時候,如果Page Cache中沒有命中你要訪問的block,就會穿透到磁盤上進行實際的IO,在你的角度來看,就是你執行完ls后,卡住了,
那么你肯定會問,我確實要保存許許多多的檔案,我該怎么辦? 其實也很簡單,多創建一些檔案夾就好了,一個目錄下別存太多,就不會有這個問題了,工程實踐中,一般的做法就是通過一級甚至是二級hash把檔案散列到多個目錄中,把單目錄檔案數量控制在十萬或萬以下,
ext的bug
貌似今天的實踐應該結束了,現在讓我們把剛剛創建的檔案全部刪掉,再看一下,
# rm -f *
# du -h
72K .
等等,什么情況?檔案夾下的檔案都已經刪了,該檔案夾為什么還占用72K的磁盤空間?
這個疑惑也伴隨了我很長時間,后來才算是解惑,問題關鍵在于ext4_dir_entry_2中的rec_len,這個變數存盤了當前整個ext4_dir_entry_2物件的長度,這樣作業系統在遍歷檔案夾的時候,就可以通過當前的指標,加上這個長度就可以找到檔案夾中下一個檔案的dir_entry了,這樣的優勢是遍歷起來非常方便,有點像是一個鏈表,一個一個穿起來的,
但是,如果要洗掉一個檔案的話,就有點小麻煩了,當前檔案結構體變數不能直接刪,否則鏈表就斷了,
Linux的做法是在洗掉檔案的時候,在其目錄中只是把inode設定為0就拉倒,并沒有回收整個ext4_dir_entry_2物件,其實和大家做工程的時候經常用到的假洗掉是一個道理,現在的xfs檔案系統好像已經沒有這個小問題了,但具體咋解決的,暫時沒有深入研究,如果你有答案,歡迎留言!

開發內功修煉之硬碟篇專輯:
- 1.磁盤開篇:扒開機械硬碟堅硬的外衣!
- 2.磁盤磁區也是隱含了技術技巧的
- 3.我們怎么解決機械硬碟既慢又容易壞的問題?
- 4.拆解固態硬碟結構
- 5.新建一個空檔案占用多少磁盤空間?
- 6.只有1個位元組的檔案實際占用多少磁盤空間
- 7.檔案過多時ls命令為什么會卡住?
- 8.理解格式化原理
- 9.read檔案一個位元組實際會發生多大的磁盤IO?
- 10.write檔案一個位元組后何時發起寫磁盤IO?
- 11.機械硬碟隨機IO慢的超乎你的想象
- 12.搭載固態硬碟的服務器究竟比搭機械硬碟快多少?
我的公眾號是「開發內功修煉」,在這里我不是單純介紹技術理論,也不只介紹實踐經驗,而是把理論與實踐結合起來,用實踐加深對理論的理解、用理論提高你的技術實踐能力,歡迎你來關注我的公眾號,也請分享給你的好友~~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/203578.html
標籤:PHP
下一篇:理解格式化原理
