Linux文件系統(tǒng)之sparse文件處理與傳輸
0. 什么是sparse文件
當(dāng)用戶(hù)申請(qǐng)一塊很大的存儲(chǔ)空間時(shí),由于最開(kāi)始并沒(méi)有寫(xiě)入數(shù)據(jù)(全是空),此時(shí)文件系統(tǒng)為了節(jié)省存儲(chǔ)資源,提高資源利用率,不會(huì)分配實(shí)際存儲(chǔ)空間,只有當(dāng)真正寫(xiě)入數(shù)據(jù)時(shí),操作系統(tǒng)才真正一點(diǎn)一點(diǎn)地分配空間,比如一次64KB。于是這個(gè)文件看起來(lái)很大,而占用空間很小,實(shí)際占用空間只與用戶(hù)填的數(shù)據(jù)量有關(guān)。該文件看起來(lái)像一個(gè)大盒子,但可能裝的東西不多,空洞很大,因此稱(chēng)為稀疏文件(Sparse file)。Sparse文件是Linux文件系統(tǒng)的一個(gè)高級(jí)特性,能夠?qū)崿F(xiàn)磁盤(pán)的超負(fù)載使用(overload)。它最經(jīng)典的應(yīng)用就是為虛擬機(jī)創(chuàng)建虛擬硬盤(pán)以及數(shù)據(jù)庫(kù)快照,比如我們使用qemu-img創(chuàng)建一個(gè)大小為20GB的raw文件(注意qcow2格式不是sparse文件):
- fgp@node1:~$ qemu-img create -f raw test.raw 20G
- Formatting 'test.raw', fmt=raw size=21474836480
- fgp@node1:~$ qemu-img info test.raw
- image: test.raw
- file format: raw
- virtual size: 20G (21474836480 bytes)
- disk size: 0
以上我們使用qemu-img創(chuàng)建了一個(gè)20G的鏡像文件,由qemu-img info顯示,virtual size為我們分配的空間大小,而disk size為實(shí)際占用的空間,最開(kāi)始并不占任何磁盤(pán)空間。
注:qemu-img create -f raw相當(dāng)于`truncate -s 20G test.raw’。
當(dāng)然也會(huì)有問(wèn)題,比如系統(tǒng)生成了一堆sparse文件,如果文件系統(tǒng)滿(mǎn)了,則這些文件都會(huì)寫(xiě)入失敗,為了避免這種情況,需要控制sparse文件的數(shù)量。
1.如何判斷是否sparse文件
除了以上的鏡像文件可能是sparse文件,其他文件類(lèi)型也有可能是sparse文件,如何判斷是否sparse文件呢?最簡(jiǎn)單的辦法是使用ls命令和du命令分別查看大小,如果二者大小不一致,則說(shuō)明是sparse文件。我們可以使用dd命令快速生成一個(gè)sparse文件:
- dd if=/dev/zero of=sparse_file bs=1M seek=1024 count=0
以上命令從第1024 * 1M處開(kāi)始寫(xiě)文件(相當(dāng)于中間空了1GB空間),寫(xiě)入/dev/zero,實(shí)際寫(xiě)入了0個(gè)塊(count=0),因此實(shí)際上并沒(méi)有寫(xiě)入任何數(shù)據(jù)。我們使用ls -lh查看其大?。?/p>
- ~$ ls -lh sparse_file
- -rw-rw-r-- 1 fgp fgp 1.0G May 26 15:47 sparse_file
可見(jiàn)該文件顯示為1G。
我們?cè)偈褂胐u -h命令查看其占用磁盤(pán)空間大小:
- ~$ du -h sparse_file
- 0 sparse_file
我們發(fā)現(xiàn)實(shí)際占用磁盤(pán)空間為0。
我們也可以直接使用ls的-s參數(shù)查看文件實(shí)際占用空間大小:
- ~$ ls -slh sparse_file
- 0 -rw-rw-r-- 1 fgp fgp 1.0G May 26 15:47 sparse_file
其中***列為實(shí)際占用磁盤(pán)空間大小,第6列為文件大小(虛擬大小)。
另外使用truncate命令可以隨意調(diào)節(jié)文件大小(如果該文件不存在則會(huì)自動(dòng)創(chuàng)建),比如:
- ~$ truncate --size 1T sparse_file
- ~$ du -h sparse_file
- 0 sparse_file
- ~$ ls -lh sparse_file
- -rw-rw-r-- 1 fgp fgp 1.0T May 26 16:09 sparse_file
以上我們把sparse_file文件大小調(diào)為1TB,實(shí)際上就是往后面追加空洞(extended part (hole) reads as zero bytes),因此不會(huì)占用實(shí)際磁盤(pán)空間。當(dāng)然也可以縮小文件大小,但是如果比文件數(shù)據(jù)占用空間還小的話(huà),就會(huì)截取數(shù)據(jù),因此部分?jǐn)?shù)據(jù)會(huì)丟失。
- truncate -s 500M sparse_file
- ~$ ls -lh sparse_file
- -rw-rw-r-- 1 fgp fgp 500M May 26 16:12 sparse_file
以上我們把該文件縮減為500MB。
2. sparse文件處理
sparse文件在處理時(shí)也存在一些問(wèn)題,比如我們使用sed對(duì)一個(gè)sparse文件進(jìn)行處理。
- fgp@node1:~/tmp$ echo "Hello World" >test.raw
- fgp@node1:~/tmp$ truncate -s 1G test.raw
- fgp@node1:~/tmp$ ls -slh
- total 68K
- 4.0K -rw-rw-r-- 1 fgp fgp 1.0G May 28 14:52 test.raw
- fgp@node1:~/tmp$ sed -i 's/Hello/HELLO/g' test.raw
- fgp@node1:~/tmp$ ls -slh
- total 1.1G
- 1.1G -rw-rw-r-- 1 fgp fgp 1.0G May 28 14:53 test.raw
以上我們使用truncate創(chuàng)建了一個(gè)sparse文件,然后通過(guò)sed命令把Hello改為HELLO,我們期望能夠保留該文件的sparse特性,但實(shí)際上我們發(fā)現(xiàn)僅僅修改了該文件的一行數(shù)據(jù),該文件的空洞被填滿(mǎn),瞬間占用磁盤(pán)空間為1G。一個(gè)只有4K大小的文件使用sed命令后變成了1G,這讓人感到莫名其妙不是嗎?
再比如我們我們使用tar命令對(duì)文件進(jìn)行歸檔:
- fgp@node1:~/tmp$ qemu-img create -f raw test.raw 1G
- Formatting 'test.raw', fmt=raw size=1073741824
- fgp@node1:~/tmp$ time tar -cf test.tar test.raw
- real 0m2.145s
- user 0m0.012s
- sys 0m1.640s
- fgp@node1:~/tmp$ time tar -cJf test.tar.xz test.raw
- real 1m0.692s
- user 0m59.060s
- sys 0m1.048s
- fgp@node1:~/tmp$ ls -lsh
- total 1.1G
- 0 -rw-r--r-- 1 fgp fgp 1.0G May 28 15:37 test.raw
- 1.1G -rw-rw-r-- 1 fgp fgp 1.1G May 28 15:37 test.tar
- 156K -rw-rw-r-- 1 fgp fgp 153K May 28 15:39 test.tar.xz
以上我們創(chuàng)建了一個(gè)1G的sparse文件,當(dāng)使用tar直接歸檔時(shí)發(fā)現(xiàn)該文件變成了非sparse文件,占用了1G的磁盤(pán)空間。而使用xz壓縮時(shí),雖然解決了存儲(chǔ)空間的問(wèn)題,同時(shí)也帶來(lái)壓縮時(shí)間開(kāi)銷(xiāo)問(wèn)題(耗費(fèi)了1分鐘的時(shí)間進(jìn)行壓縮)。
接下來(lái)介紹下熟悉的經(jīng)典命令cp,cp命令可謂無(wú)人不知。眾所周知,它用于在本地拷貝文件。值得慶幸(為什么慶幸,因?yàn)椴⒉皇撬械拿疃贾С衷撎匦?的是cp命令能夠自動(dòng)探測(cè)文件是否sparse文件,空洞數(shù)據(jù)不會(huì)拷貝,并且能夠保留sparce文件副本的稀疏性質(zhì):
- fgp@node1:~$ cp sparse_file sparse_file.copy
- fgp@node1:~$ ls -slh sparse_file*
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file.copy
我們看看和cp命令類(lèi)似的命令scp,scp用于遠(yuǎn)程拷貝文件(遠(yuǎn)程傳輸文件):
- fgp@node1:~$ scp sparse_file localhost:~/sparse_file.copy
- sparse_file 100% 2048MB 97.5MB/s 00:21
- fgp@node1:~$ ls -slh sparse_file*
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file
- 2.1G -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:42 sparse_file.copy
我們發(fā)現(xiàn)scp不能識(shí)別sparse文件,傳輸一個(gè)sparse文件時(shí)會(huì)自動(dòng)填滿(mǎn)空洞,發(fā)送整個(gè)文件內(nèi)容。
其實(shí)cp命令有一個(gè)針對(duì)sparse文件拷貝優(yōu)化的參數(shù)--sparse=WHEN,其中WHEN的合法值為auto、always、never,默認(rèn)為auto,能自動(dòng)識(shí)別是否sparse文件。如果設(shè)置為never則會(huì)自動(dòng)填滿(mǎn)數(shù)據(jù),拷貝整個(gè)文件:
- fgp@node1:~$ cp --sparse=never sparse_file sparse_file.copy.2
- fgp@node1:~$ ls -lhs sparse_file*
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file
- 2.1G -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:42 sparse_file.copy
- 2.1G -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:50 sparse_file.copy.2
可見(jiàn)sparse_file.copy.2填滿(mǎn)了空洞,相當(dāng)于把sparse文件轉(zhuǎn)化成了非sparse文件。
如果指定為always,則cp會(huì)嘗試把文件轉(zhuǎn)換為sparse文件,減少磁盤(pán)占用空間:
- fgp@node1:~$ cp --sparse=always sparse_file.copy sparse_file.copy.3
- fgp@node1:~$ ls -lsh sparse_file*
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file
- 2.1G -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:42 sparse_file.copy
- 2.1G -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:50 sparse_file.copy.2
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:52 sparse_file.copy.3
由結(jié)果發(fā)現(xiàn),我們把非sparse文件sparse_file.copy轉(zhuǎn)成了sparse文件sparse_file.copy.3。
注:cp命令黑科技,cp實(shí)現(xiàn)sparse文件的相互轉(zhuǎn)換!
其實(shí)除了cp命令,我們上面的tar命令也支持–sparse參數(shù):
- fgp@node1:~/tmp$ time tar -cSf test.tar test.raw
- real 0m0.002s
- user 0m0.000s
- sys 0m0.000s
- fgp@node1:~/tmp$ time tar -cSJf test.tar.xz test.raw
- real 0m0.011s
- user 0m0.000s
- sys 0m0.008s
- fgp@node1:~/tmp$ ls -slh
- total 16K
- 0 -rw-r--r-- 1 fgp fgp 1.0G May 28 15:37 test.raw
- 12K -rw-rw-r-- 1 fgp fgp 10K May 28 15:42 test.tar
- 4.0K -rw-rw-r-- 1 fgp fgp 184 May 28 15:43 test.tar.xz
對(duì)比前面的結(jié)果,我們發(fā)現(xiàn)使用tar的-S(–sparse)參數(shù)很好的處理sparse文件。
另外cpio也支持同樣的參數(shù),但可惜的是scp命令不支持,因此我們使用scp遠(yuǎn)程傳輸大量的sparse文件時(shí)效率極低,并且浪費(fèi)大量網(wǎng)絡(luò)空間。比如我們經(jīng)常使用qemu-img創(chuàng)建了一個(gè)40GB的raw文件,然后需要拷貝鏡像到其他機(jī)器上,雖然該文件可能只占了1GB左右的磁盤(pán)空間,可使用scp需要傳輸40GB的空間,并且遠(yuǎn)程需要預(yù)留40GB的磁盤(pán)空間。那有沒(méi)有高效傳輸sparse文件的方法呢?實(shí)際上,很可惜,好像并沒(méi)有,不過(guò)有比較好的方法,請(qǐng)看下一節(jié)內(nèi)容。
3.相對(duì)高效傳輸sparse文件的方法
我們前面說(shuō)了scp不支持sparse文件的處理,好在rsync命令支持sparse文件處理:
- fgp@node1:~$ rsync -av --sparse --progress sparse_file localhost:~/sparse_file.copy
- fgp@localhost's password:
- sending incremental file list
- sparse_file
- 2,147,483,648 100% 74.67MB/s 0:00:27 (xfr#1, to-chk=0/1)
- sent 2,148,008,037 bytes received 35 bytes 66,092,556.06 bytes/sec
- total size is 2,147,483,648 speedup is 1.00
- fgp@node1:~$ ls -lhs sparse_file*
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file
- 0 -rw-rw-r-- 1 fgp fgp 2.0G May 26 16:39 sparse_file.copy
遺憾的是,雖然目標(biāo)文件保留了其sparse特性,節(jié)省了目標(biāo)主機(jī)的存儲(chǔ)空間,但并沒(méi)有節(jié)省網(wǎng)絡(luò)傳輸帶寬,依然傳輸了2GB的數(shù)據(jù),rsync不能過(guò)濾掉空洞數(shù)據(jù)的傳輸。
值得一提的是rsync有一個(gè)參數(shù)--inplace,這個(gè)參數(shù)能夠探測(cè)源文件和目標(biāo)文件是否修改的塊,傳輸時(shí)只傳遞修改的塊,當(dāng)然***次傳輸文件時(shí),這個(gè)參數(shù)并沒(méi)有什么用。但可惜的是–sparse參數(shù)和–inplace參數(shù)不能同時(shí)使用。通常做法是***次傳輸文件時(shí),使用–sparse參數(shù),之后如果對(duì)文件進(jìn)行了修改,需要同步遠(yuǎn)程時(shí),使用–inplace參數(shù),它只會(huì)在原文件的基礎(chǔ)上傳輸更新的塊。(可以先在遠(yuǎn)程目標(biāo)機(jī)器上先使用truncate命令創(chuàng)建一個(gè)同名的sparse文件,再使用–inplace參數(shù)傳遞)。
當(dāng)然如果我們傳輸?shù)氖晴R像文件,可以通過(guò)qemu-img把raw格式在本地轉(zhuǎn)化為qcow2格式后再傳輸:
- fgp@node1:~/tmp$ ls -lsh
- total 0
- 0 -rw-rw-r-- 1 fgp fgp 10G May 28 15:00 test.raw
- fgp@node1:~/tmp$ qemu-img convert -f raw -O qcow2 test.raw test.qcow2
- fgp@node1:~/tmp$ ls -lsh
- total 196K
- 196K -rw-r--r-- 1 fgp fgp 193K May 28 15:12 test.qcow2
- 0 -rw-rw-r-- 1 fgp fgp 10G May 28 15:00 test.raw
轉(zhuǎn)化成qcow2格式后,不再是sparse文件,因此不會(huì)存在以上問(wèn)題。由以上輸出我們發(fā)現(xiàn),該文件只有196K,因此傳輸量大幅度減少。
【本文是51CTO專(zhuān)欄作者“付廣平”的原創(chuàng)文章,如需轉(zhuǎn)載請(qǐng)通過(guò)51CTO獲得聯(lián)系】