Btrfs 詳解:壓縮
這篇文章將探索 Btrfs 中的透明文件系統(tǒng)壓縮,以及它如何幫助節(jié)省存儲(chǔ)空間。這篇文章是《Btrfs 詳解》系列文章中的一篇。從 Fedora Linux 33 開(kāi)始,Btrfs 就是 Fedora Workstation 和 Fedora Silverblue 的默認(rèn)文件系統(tǒng)。
如果你錯(cuò)過(guò)了,這里是本系列的上一篇文章:Btrfs 詳解:快照。
簡(jiǎn)介
很多人都經(jīng)歷過(guò)存儲(chǔ)空間用完的情況。也許你想從互聯(lián)網(wǎng)下載一個(gè)大文件,或者你需要快速?gòu)哪愕氖謾C(jī)中復(fù)制些照片,然后操作突然失敗。雖然存儲(chǔ)空間成本正在穩(wěn)步降低,但越來(lái)越多的設(shè)備要么制造時(shí)就是固定數(shù)量的存儲(chǔ)容量,要么最終用戶(hù)難以擴(kuò)展其存儲(chǔ)容量。
但當(dāng)你的存儲(chǔ)空間不足時(shí)你可以做什么呢?也許你會(huì)求助于云存儲(chǔ),或者你可以隨身攜帶一些外部存儲(chǔ)設(shè)備。
在這篇文章里我會(huì)研究該問(wèn)題的另一種解決方案:透明的文件系統(tǒng)壓縮,這是 Btrfs 的一個(gè)特性。理想情況下,這將解決你的存儲(chǔ)問(wèn)題,同時(shí)幾乎不需要對(duì)你的系統(tǒng)進(jìn)行修改!讓我們來(lái)看看是如何做到的。
透明壓縮的解釋
首先,讓我們來(lái)探尋 透明
這不僅是一個(gè)乏味的過(guò)程,而且也暫時(shí)打破了你之前節(jié)省的空間。再者,你最終解壓了你不想訪問(wèn)的那部分文件內(nèi)容。明顯有比這更好的方法!
相反,透明壓縮發(fā)生在文件系統(tǒng)級(jí)別。在這里,壓縮的文件對(duì)用戶(hù)看起來(lái)像常規(guī)的未壓縮文件一樣。但是,它們是被壓縮后存儲(chǔ)在硬盤(pán)上的。這之所以可行,是因?yàn)椴僮飨到y(tǒng)僅僅選擇性地訪問(wèn)那部分文件,并且確保在向磁盤(pán)寫(xiě)入更新時(shí)再次壓縮它們。
這里的壓縮是透明的在于它不被用戶(hù)感知,除了在文件訪問(wèn)時(shí)可能的 CPU 負(fù)載小量增加。因此,你可以應(yīng)用在已有的系統(tǒng)而不是進(jìn)行硬件修改或者求助于云存儲(chǔ)。
壓縮算法對(duì)比
Btrfs 提供了多個(gè)壓縮算法的選擇。出于技術(shù)原因它不能選用任意的壓縮算法。它現(xiàn)在支持:
- zstd
- lzo
- zlib
好消息是,由于透明壓縮的工作原理,你不需要安裝這些程序供 Btrfs 使用。在下面的文章里,你會(huì)看到如何去運(yùn)行一個(gè)簡(jiǎn)單的性能測(cè)試來(lái)對(duì)比壓縮算法。但是,為了運(yùn)行性能測(cè)試,你必須安裝必要的可執(zhí)行文件。事后不需要留著它們,所以你將使用 Podman 容器來(lái)確保不會(huì)在系統(tǒng)中留下任何痕跡。
注意 :因?yàn)?Btrfs 使用的壓縮依賴(lài)于內(nèi)核對(duì)這些壓縮算法的(重新)實(shí)現(xiàn),用戶(hù)空間版本的算法得出的結(jié)果應(yīng)該認(rèn)為是粗略估計(jì)。
因?yàn)橐淮未吻弥貜?fù)的命令是枯燥的工作,我已經(jīng)在 Gitlab 上準(zhǔn)備了一個(gè)可以運(yùn)行的 Bash 腳本 (https://gitlab.com/hartang/btrfs-compression-test)。這會(huì)用上面提到的每個(gè)算法在不同的壓縮級(jí)別運(yùn)行一次簡(jiǎn)單的壓縮和解壓縮。
首先,下載腳本:
$ curl -LO https://gitlab.com/hartang/btrfs-compression-test/-/raw/main/btrfs_compression_test.sh
下一步,啟動(dòng)一個(gè) Fedora Linux 容器去掛載你當(dāng)前的工作目錄,以便你可以和主機(jī)交換文件同時(shí)在那里運(yùn)行腳本:
$ podman run --rm -it --security-opt label=disable -v "$PWD:$PWD" \
-w "$PWD" registry.fedoraproject.org/fedora:37
最后運(yùn)行腳本:
$ chmod +x ./btrfs_compression_test.sh
$ ./btrfs_compression_test.sh
在我機(jī)器上的輸出是這樣:
[INFO] Using file 'glibc-2.36.tar' as compression target
[INFO] Target file 'glibc-2.36.tar' not found, downloading now...
################################################################### 100.0%
[ OK ] Download successful!
[INFO] Copying 'glibc-2.36.tar' to '/tmp/tmp.vNBWYg1Vol/' for benchmark...
[INFO] Installing required utilities
[INFO] Testing compression for 'zlib'
Level | Time (compress) | Compression Ratio | Time (decompress)
-------+-----------------+-------------------+-------------------
1 | 0.322 s | 18.324 % | 0.659 s
2 | 0.342 s | 17.738 % | 0.635 s
3 | 0.473 s | 17.181 % | 0.647 s
4 | 0.505 s | 16.101 % | 0.607 s
5 | 0.640 s | 15.270 % | 0.590 s
6 | 0.958 s | 14.858 % | 0.577 s
7 | 1.198 s | 14.716 % | 0.561 s
8 | 2.577 s | 14.619 % | 0.571 s
9 | 3.114 s | 14.605 % | 0.570 s
[INFO] Testing compression for 'zstd'
Level | Time (compress) | Compression Ratio | Time (decompress)
-------+-----------------+-------------------+-------------------
1 | 0.492 s | 14.831 % | 0.313 s
2 | 0.607 s | 14.008 % | 0.341 s
3 | 0.709 s | 13.195 % | 0.318 s
4 | 0.683 s | 13.108 % | 0.306 s
5 | 1.300 s | 11.825 % | 0.292 s
6 | 1.824 s | 11.298 % | 0.286 s
7 | 2.215 s | 11.052 % | 0.284 s
8 | 2.834 s | 10.619 % | 0.294 s
9 | 3.079 s | 10.408 % | 0.272 s
10 | 4.355 s | 10.254 % | 0.282 s
11 | 6.161 s | 10.167 % | 0.283 s
12 | 6.670 s | 10.165 % | 0.304 s
13 | 12.471 s | 10.183 % | 0.279 s
14 | 15.619 s | 10.075 % | 0.267 s
15 | 21.387 s | 9.989 % | 0.270 s
[INFO] Testing compression for 'lzo'
Level | Time (compress) | Compression Ratio | Time (decompress)
-------+-----------------+-------------------+-------------------
1 | 0.447 s | 25.677 % | 0.438 s
2 | 0.448 s | 25.582 % | 0.438 s
3 | 0.444 s | 25.582 % | 0.441 s
4 | 0.444 s | 25.582 % | 0.444 s
5 | 0.445 s | 25.582 % | 0.453 s
6 | 0.438 s | 25.582 % | 0.444 s
7 | 8.990 s | 18.666 % | 0.410 s
8 | 34.233 s | 18.463 % | 0.405 s
9 | 41.328 s | 18.450 % | 0.426 s
[INFO] Cleaning up...
[ OK ] Benchmark complete!
重要的是在根據(jù)腳本得出的數(shù)據(jù)做決定之前注意這些事情:
- 不是所有的文件壓縮效果都一樣好。像圖片或電影這種已經(jīng)壓縮過(guò)的現(xiàn)代多媒體格式不會(huì)壓縮得更小。
- 腳本中壓縮和解壓縮各進(jìn)行一次。重復(fù)運(yùn)行會(huì)產(chǎn)生稍微不同的輸出。因此,時(shí)間應(yīng)該被理解為是估計(jì),而不是準(zhǔn)確的測(cè)量。
鑒于輸出的數(shù)據(jù),我決定在我的系統(tǒng)上使用壓縮級(jí)別 3 的 zstd 壓縮算法。依據(jù)你的需求,你可能想使用更高的壓縮級(jí)別(比如,如果你存儲(chǔ)設(shè)備相當(dāng)?shù)穆R浪憧蛇_(dá)到的讀/寫(xiě)速度,可以將源存檔大小(約 260MB)除以(解)壓縮時(shí)間。
壓縮測(cè)試默認(rèn)是對(duì) GNU libc 2.36 源碼進(jìn)行的。如果你想看看對(duì)指定文件的效果,你可以通過(guò)第一個(gè)參數(shù)傳遞文件路徑給腳本。記住文件一定要可以在容器內(nèi)訪問(wèn)才行。
如果你想要測(cè)試其他東西或者執(zhí)行更加詳細(xì)的測(cè)試,可以閱讀腳本的源碼,根據(jù)需要修改它。
配置 Btrfs 壓縮
Btrfs 里的透明文件系統(tǒng)壓縮可以通過(guò)幾種方式配置:
- 作為掛載文件系統(tǒng)的掛載選項(xiàng)(可用于相同 Btrfs 文件系統(tǒng)的所有子卷)
- 通過(guò) Btrfs 文件屬性
- 在
btrfs filesystem defrag
時(shí)(不是永久的,不在這里介紹) - 通過(guò)
chattr
文件屬性接口(不在這里介紹)
我只會(huì)介紹其中前兩個(gè)。
在掛載時(shí)開(kāi)啟壓縮
有一個(gè) Btrfs 掛載選項(xiàng)可以開(kāi)啟文件壓縮:
$ sudo mount -o compress=<ALGORITHM>:<LEVEL> ...
例如,去掛載一個(gè)文件系統(tǒng),并使用等級(jí) 3 的 ztsd
算法去壓縮,你可以寫(xiě)成:
$ sudo mount -o compress=zstd:3 ...
設(shè)置壓縮等級(jí)是可選的。重要的是注意到 compress
掛載選項(xiàng)應(yīng)用到整個(gè) Btrfs 文件系統(tǒng)和它所有的子卷。此外,這是目前唯一支持的指定壓縮等級(jí)的方式。
為了對(duì)文件系統(tǒng)的根應(yīng)用壓縮,必須在 /etc/fstab
上指定。例如,F(xiàn)edora Linux 安裝器,默認(rèn)啟用級(jí)別 1 的 zstd
壓縮,在 /etc/fstab
里是這樣:
$ cat /etc/fstab
[ ... ]
UUID=47b03671-39f1-43a7-b0a7-db733bfb47ff / btrfs subvol=root,compress=zstd:1,[ ... ] 0 0
啟用單個(gè)文件壓縮
另外一種方式指定壓縮的方法是通過(guò) Btrfs 文件系統(tǒng)屬性。使用下面的命令去查看文件、目錄或子卷的壓縮設(shè)置:
$ btrfs property get <PATH> compression
類(lèi)似的,你可以像這樣配置壓縮:
$ sudo btrfs property set <PATH> compression <VALUE>
例如,對(duì)在 /etc
下所有文件啟用 zlib
壓縮:
$ sudo btrfs property set /etc compression zlib
你可以通過(guò) man btrfs-property
得到支持值的列表。記住這個(gè)接口不允許指定壓縮級(jí)別。除此之外,如果設(shè)置了一個(gè)壓縮屬性,它會(huì)覆蓋掛載時(shí)的其他壓縮配置。
壓縮已有文件
在這時(shí),如果你對(duì)現(xiàn)有文件系統(tǒng)采用壓縮,然后通過(guò) df
或類(lèi)似命令檢查空間利用率,你會(huì)發(fā)現(xiàn)什么都沒(méi)變。這是因?yàn)?Btrfs 自身不會(huì) “重新壓縮” 所有已有的文件。壓縮只會(huì)發(fā)生在往磁盤(pán)寫(xiě)新數(shù)據(jù)的時(shí)候。有一些方式去執(zhí)行顯式的重壓縮:
- 等待,什么都不做:只要文件被修改并被寫(xiě)回磁盤(pán),Btrfs 根據(jù)配置壓縮新寫(xiě)入的文件內(nèi)容。如果我們等待足夠長(zhǎng),越來(lái)越多的文件被重寫(xiě),在某個(gè)時(shí)間點(diǎn)就會(huì)被壓縮。
- 移動(dòng)文件到另一個(gè)文件系統(tǒng)然后移動(dòng)回來(lái):取決于你想壓縮哪些文件,這可能是相當(dāng)乏味的選項(xiàng)。
- 執(zhí)行一次 Btrfs 碎片整理。
最后一個(gè)選項(xiàng)可能是最方便的,但是它會(huì)對(duì)已經(jīng)包含快照的 Btrfs 文件系統(tǒng)提出警告:它會(huì)破壞快照間的共享范圍。換句話(huà)來(lái)說(shuō),兩個(gè)快照間所有的共享內(nèi)容,或者一個(gè)快照和它的父子卷,在碎片整理操作后將保存多份。
因此,如果你在你的文件系統(tǒng)里已經(jīng)有很多快照,你不應(yīng)該對(duì)整個(gè)文件系統(tǒng)運(yùn)行碎片整理。這也沒(méi)有必要,因?yàn)槿绻阆氲脑?huà),Btrfs 可以對(duì)特定的目錄或者單個(gè)文件進(jìn)行碎片整理。
你可以使用以下命令去執(zhí)行一次碎片整理:
$ sudo btrfs filesystem defragment -r /path/to/defragment
例如,你想像這樣去整理你主目錄的碎片:
$ sudo btrfs filesystem defragment -r "$HOME"
如果有疑問(wèn),最好從碎片整理單個(gè)大文件開(kāi)始,并在監(jiān)視文件系統(tǒng)上的可用空間的同時(shí)繼續(xù)處理越來(lái)越大的目錄。
測(cè)量文件系統(tǒng)壓縮
有時(shí),你可能會(huì)想,文件系統(tǒng)壓縮為你節(jié)省了多少空間。但如何判斷呢?首先,要知道一個(gè) Btrfs 文件系統(tǒng)是否在掛載時(shí)啟用了壓縮,你可以使用以下命令:
$ findmnt -vno OPTIONS /path/to/mountpoint | grep compress
如果你得到了結(jié)果,那么給定掛載點(diǎn)的文件系統(tǒng)就使用了壓縮!下一步,compsize
命令會(huì)告訴你你的文件需要多少空間:
$ sudo compsize -x /path/to/examine
在我的主目錄,結(jié)果是這樣:
$ sudo compsize -x "$HOME"
Processed 942853 files, 550658 regular extents (799985 refs), 462779 inline.
Type Perc Disk Usage Uncompressed Referenced
TOTAL 81% 74G 91G 111G
none 100% 67G 67G 77G
zstd 28% 6.6G 23G 33G
每一行告訴你應(yīng)用到文件的壓縮 “類(lèi)型” 。* TOTAL
是下面所有行的總計(jì)。
另一方面,這些列告訴你我們的文件需要多少空間:
Disk Usage
是實(shí)際分配在硬盤(pán)上的空間,Uncompressed
是如果沒(méi)有壓縮,文件所需要的空間,Referenced
是所有未壓縮文件加起來(lái)的總大小。
Referenced
可以與數(shù)據(jù) Uncompressed
不同,比如一個(gè)文件之前被重復(fù)了,或者有快照共享內(nèi)容。在上面的例子,你可以看到在我的硬盤(pán)上總計(jì) 91 GB 的未壓縮文件僅占據(jù)了 74 GB 的存儲(chǔ)。取決于在目錄里存儲(chǔ)的文件類(lèi)型和應(yīng)用的壓縮等級(jí),這些數(shù)字可以有很大差異。
文件壓縮的其它注意事項(xiàng)
Btrfs 使用啟發(fā)式算法去探測(cè)壓縮文件。這是因?yàn)閴嚎s文件通常效果不好,所以沒(méi)有必要浪費(fèi) CPU 周期去嘗試進(jìn)一步的壓縮。為了這個(gè)目的,Btrfs 在寫(xiě)入壓縮數(shù)據(jù)到磁盤(pán)之前測(cè)量壓縮率。如果文件的第一部分壓縮效果不好,文件被標(biāo)記為不可壓縮并且不會(huì)有后續(xù)的壓縮。
如果出于某些原因,你想 Btrfs 壓縮所有寫(xiě)入的數(shù)據(jù),你可以通過(guò) compress-force
選項(xiàng)掛載一個(gè) Btrfs 文件系統(tǒng),像這樣:
$ sudo mount -o compress-force=zstd:3 ...
當(dāng)像這樣配置,Btrfs 會(huì)用等級(jí) 3 的 zstd
算法壓縮所有寫(xiě)入磁盤(pán)的數(shù)據(jù)。
一個(gè)重要的注意事項(xiàng)是掛載一個(gè)有很多數(shù)據(jù)并開(kāi)啟壓縮的 Btrfs 文件系統(tǒng)會(huì)比沒(méi)開(kāi)啟壓縮耗時(shí)更長(zhǎng)。這是有技術(shù)上的原因的,而且這是一個(gè)不會(huì)影響文件系統(tǒng)操作的正常行為。
總結(jié)
本文詳細(xì)介紹了 Btrfs 中的透明文件系統(tǒng)壓縮。這是一種內(nèi)置的、相對(duì)廉價(jià)的方法,可以在不需要修改的情況下從現(xiàn)有硬件中獲得一些額外的存儲(chǔ)空間。
本系列文章的下一篇將討論:
- Qgroups - 限制文件系統(tǒng)大小
- RAID - 替換 mdadm 配置
(LCTT 譯注:后繼文章尚未發(fā)布,一旦發(fā)布我們會(huì)盡快翻譯。)
如果你想了解與 Btrfs 相關(guān)的其他主題,請(qǐng)查看 Btrfs 維基 [1] 和文檔 [2]。如果你還沒(méi)有閱讀本系列的前三篇文章,請(qǐng)不要忘記去看看!