Btrfs 詳解:壓縮
這篇文章將探索 Btrfs 中的透明文件系統(tǒng)壓縮,以及它如何幫助節(jié)省存儲空間。這篇文章是《Btrfs 詳解》系列文章中的一篇。從 Fedora Linux 33 開始,Btrfs 就是 Fedora Workstation 和 Fedora Silverblue 的默認(rèn)文件系統(tǒng)。
如果你錯過了,這里是本系列的上一篇文章:Btrfs 詳解:快照。
簡介
很多人都經(jīng)歷過存儲空間用完的情況。也許你想從互聯(lián)網(wǎng)下載一個大文件,或者你需要快速從你的手機(jī)中復(fù)制些照片,然后操作突然失敗。雖然存儲空間成本正在穩(wěn)步降低,但越來越多的設(shè)備要么制造時就是固定數(shù)量的存儲容量,要么最終用戶難以擴(kuò)展其存儲容量。
但當(dāng)你的存儲空間不足時你可以做什么呢?也許你會求助于云存儲,或者你可以隨身攜帶一些外部存儲設(shè)備。
在這篇文章里我會研究該問題的另一種解決方案:透明的文件系統(tǒng)壓縮,這是 Btrfs 的一個特性。理想情況下,這將解決你的存儲問題,同時幾乎不需要對你的系統(tǒng)進(jìn)行修改!讓我們來看看是如何做到的。
透明壓縮的解釋
首先,讓我們來探尋 透明
這不僅是一個乏味的過程,而且也暫時打破了你之前節(jié)省的空間。再者,你最終解壓了你不想訪問的那部分文件內(nèi)容。明顯有比這更好的方法!
相反,透明壓縮發(fā)生在文件系統(tǒng)級別。在這里,壓縮的文件對用戶看起來像常規(guī)的未壓縮文件一樣。但是,它們是被壓縮后存儲在硬盤上的。這之所以可行,是因為操作系統(tǒng)僅僅選擇性地訪問那部分文件,并且確保在向磁盤寫入更新時再次壓縮它們。
這里的壓縮是透明的在于它不被用戶感知,除了在文件訪問時可能的 CPU 負(fù)載小量增加。因此,你可以應(yīng)用在已有的系統(tǒng)而不是進(jìn)行硬件修改或者求助于云存儲。
壓縮算法對比
Btrfs 提供了多個壓縮算法的選擇。出于技術(shù)原因它不能選用任意的壓縮算法。它現(xiàn)在支持:
- zstd
- lzo
- zlib
好消息是,由于透明壓縮的工作原理,你不需要安裝這些程序供 Btrfs 使用。在下面的文章里,你會看到如何去運(yùn)行一個簡單的性能測試來對比壓縮算法。但是,為了運(yùn)行性能測試,你必須安裝必要的可執(zhí)行文件。事后不需要留著它們,所以你將使用 Podman 容器來確保不會在系統(tǒng)中留下任何痕跡。
注意 :因為 Btrfs 使用的壓縮依賴于內(nèi)核對這些壓縮算法的(重新)實(shí)現(xiàn),用戶空間版本的算法得出的結(jié)果應(yīng)該認(rèn)為是粗略估計。
因為一次次敲重復(fù)的命令是枯燥的工作,我已經(jīng)在 Gitlab 上準(zhǔn)備了一個可以運(yùn)行的 Bash 腳本 (https://gitlab.com/hartang/btrfs-compression-test)。這會用上面提到的每個算法在不同的壓縮級別運(yùn)行一次簡單的壓縮和解壓縮。
首先,下載腳本:
$ curl -LO https://gitlab.com/hartang/btrfs-compression-test/-/raw/main/btrfs_compression_test.sh
下一步,啟動一個 Fedora Linux 容器去掛載你當(dāng)前的工作目錄,以便你可以和主機(jī)交換文件同時在那里運(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)壓縮過的現(xiàn)代多媒體格式不會壓縮得更小。
- 腳本中壓縮和解壓縮各進(jìn)行一次。重復(fù)運(yùn)行會產(chǎn)生稍微不同的輸出。因此,時間應(yīng)該被理解為是估計,而不是準(zhǔn)確的測量。
鑒于輸出的數(shù)據(jù),我決定在我的系統(tǒng)上使用壓縮級別 3 的 zstd 壓縮算法。依據(jù)你的需求,你可能想使用更高的壓縮級別(比如,如果你存儲設(shè)備相當(dāng)?shù)穆?。要估算可達(dá)到的讀/寫速度,可以將源存檔大?。s 260MB)除以(解)壓縮時間。
壓縮測試默認(rèn)是對 GNU libc 2.36 源碼進(jìn)行的。如果你想看看對指定文件的效果,你可以通過第一個參數(shù)傳遞文件路徑給腳本。記住文件一定要可以在容器內(nèi)訪問才行。
如果你想要測試其他東西或者執(zhí)行更加詳細(xì)的測試,可以閱讀腳本的源碼,根據(jù)需要修改它。
配置 Btrfs 壓縮
Btrfs 里的透明文件系統(tǒng)壓縮可以通過幾種方式配置:
- 作為掛載文件系統(tǒng)的掛載選項(可用于相同 Btrfs 文件系統(tǒng)的所有子卷)
- 通過 Btrfs 文件屬性
- 在
btrfs filesystem defrag
時(不是永久的,不在這里介紹) - 通過
chattr
文件屬性接口(不在這里介紹)
我只會介紹其中前兩個。
在掛載時開啟壓縮
有一個 Btrfs 掛載選項可以開啟文件壓縮:
$ sudo mount -o compress=<ALGORITHM>:<LEVEL> ...
例如,去掛載一個文件系統(tǒng),并使用等級 3 的 ztsd
算法去壓縮,你可以寫成:
$ sudo mount -o compress=zstd:3 ...
設(shè)置壓縮等級是可選的。重要的是注意到 compress
掛載選項應(yīng)用到整個 Btrfs 文件系統(tǒng)和它所有的子卷。此外,這是目前唯一支持的指定壓縮等級的方式。
為了對文件系統(tǒng)的根應(yīng)用壓縮,必須在 /etc/fstab
上指定。例如,F(xiàn)edora Linux 安裝器,默認(rèn)啟用級別 1 的 zstd
壓縮,在 /etc/fstab
里是這樣:
$ cat /etc/fstab
[ ... ]
UUID=47b03671-39f1-43a7-b0a7-db733bfb47ff / btrfs subvol=root,compress=zstd:1,[ ... ] 0 0
啟用單個文件壓縮
另外一種方式指定壓縮的方法是通過 Btrfs 文件系統(tǒng)屬性。使用下面的命令去查看文件、目錄或子卷的壓縮設(shè)置:
$ btrfs property get <PATH> compression
類似的,你可以像這樣配置壓縮:
$ sudo btrfs property set <PATH> compression <VALUE>
例如,對在 /etc
下所有文件啟用 zlib
壓縮:
$ sudo btrfs property set /etc compression zlib
你可以通過 man btrfs-property
得到支持值的列表。記住這個接口不允許指定壓縮級別。除此之外,如果設(shè)置了一個壓縮屬性,它會覆蓋掛載時的其他壓縮配置。
壓縮已有文件
在這時,如果你對現(xiàn)有文件系統(tǒng)采用壓縮,然后通過 df
或類似命令檢查空間利用率,你會發(fā)現(xiàn)什么都沒變。這是因為 Btrfs 自身不會 “重新壓縮” 所有已有的文件。壓縮只會發(fā)生在往磁盤寫新數(shù)據(jù)的時候。有一些方式去執(zhí)行顯式的重壓縮:
- 等待,什么都不做:只要文件被修改并被寫回磁盤,Btrfs 根據(jù)配置壓縮新寫入的文件內(nèi)容。如果我們等待足夠長,越來越多的文件被重寫,在某個時間點(diǎn)就會被壓縮。
- 移動文件到另一個文件系統(tǒng)然后移動回來:取決于你想壓縮哪些文件,這可能是相當(dāng)乏味的選項。
- 執(zhí)行一次 Btrfs 碎片整理。
最后一個選項可能是最方便的,但是它會對已經(jīng)包含快照的 Btrfs 文件系統(tǒng)提出警告:它會破壞快照間的共享范圍。換句話來說,兩個快照間所有的共享內(nèi)容,或者一個快照和它的父子卷,在碎片整理操作后將保存多份。
因此,如果你在你的文件系統(tǒng)里已經(jīng)有很多快照,你不應(yīng)該對整個文件系統(tǒng)運(yùn)行碎片整理。這也沒有必要,因為如果你想的話,Btrfs 可以對特定的目錄或者單個文件進(jìn)行碎片整理。
你可以使用以下命令去執(zhí)行一次碎片整理:
$ sudo btrfs filesystem defragment -r /path/to/defragment
例如,你想像這樣去整理你主目錄的碎片:
$ sudo btrfs filesystem defragment -r "$HOME"
如果有疑問,最好從碎片整理單個大文件開始,并在監(jiān)視文件系統(tǒng)上的可用空間的同時繼續(xù)處理越來越大的目錄。
測量文件系統(tǒng)壓縮
有時,你可能會想,文件系統(tǒng)壓縮為你節(jié)省了多少空間。但如何判斷呢?首先,要知道一個 Btrfs 文件系統(tǒng)是否在掛載時啟用了壓縮,你可以使用以下命令:
$ findmnt -vno OPTIONS /path/to/mountpoint | grep compress
如果你得到了結(jié)果,那么給定掛載點(diǎn)的文件系統(tǒng)就使用了壓縮!下一步,compsize
命令會告訴你你的文件需要多少空間:
$ 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)用到文件的壓縮 “類型” 。* TOTAL
是下面所有行的總計。
另一方面,這些列告訴你我們的文件需要多少空間:
Disk Usage
是實(shí)際分配在硬盤上的空間,Uncompressed
是如果沒有壓縮,文件所需要的空間,Referenced
是所有未壓縮文件加起來的總大小。
Referenced
可以與數(shù)據(jù) Uncompressed
不同,比如一個文件之前被重復(fù)了,或者有快照共享內(nèi)容。在上面的例子,你可以看到在我的硬盤上總計 91 GB 的未壓縮文件僅占據(jù)了 74 GB 的存儲。取決于在目錄里存儲的文件類型和應(yīng)用的壓縮等級,這些數(shù)字可以有很大差異。
文件壓縮的其它注意事項
Btrfs 使用啟發(fā)式算法去探測壓縮文件。這是因為壓縮文件通常效果不好,所以沒有必要浪費(fèi) CPU 周期去嘗試進(jìn)一步的壓縮。為了這個目的,Btrfs 在寫入壓縮數(shù)據(jù)到磁盤之前測量壓縮率。如果文件的第一部分壓縮效果不好,文件被標(biāo)記為不可壓縮并且不會有后續(xù)的壓縮。
如果出于某些原因,你想 Btrfs 壓縮所有寫入的數(shù)據(jù),你可以通過 compress-force
選項掛載一個 Btrfs 文件系統(tǒng),像這樣:
$ sudo mount -o compress-force=zstd:3 ...
當(dāng)像這樣配置,Btrfs 會用等級 3 的 zstd
算法壓縮所有寫入磁盤的數(shù)據(jù)。
一個重要的注意事項是掛載一個有很多數(shù)據(jù)并開啟壓縮的 Btrfs 文件系統(tǒng)會比沒開啟壓縮耗時更長。這是有技術(shù)上的原因的,而且這是一個不會影響文件系統(tǒng)操作的正常行為。
總結(jié)
本文詳細(xì)介紹了 Btrfs 中的透明文件系統(tǒng)壓縮。這是一種內(nèi)置的、相對廉價的方法,可以在不需要修改的情況下從現(xiàn)有硬件中獲得一些額外的存儲空間。
本系列文章的下一篇將討論:
- Qgroups - 限制文件系統(tǒng)大小
- RAID - 替換 mdadm 配置
(LCTT 譯注:后繼文章尚未發(fā)布,一旦發(fā)布我們會盡快翻譯。)
如果你想了解與 Btrfs 相關(guān)的其他主題,請查看 Btrfs 維基 [1] 和文檔 [2]。如果你還沒有閱讀本系列的前三篇文章,請不要忘記去看看!