Btrfs 詳解:壓縮

這篇文章將探索 Btrfs 中的透明文件系統壓縮,以及它如何幫助節省存儲空間。這篇文章是《Btrfs 詳解》系列文章中的一篇。從 Fedora Linux 33 開始,Btrfs 就是 Fedora Workstation 和 Fedora Silverblue 的默認文件系統。

如果你錯過了,這裏是本系列的上一篇文章:Btrfs 詳解:快照

簡介

很多人都經歷過存儲空間用完的情況。也許你想從互聯網下載一個大文件,或者你需要快速從你的手機中複製些照片,然後操作突然失敗。雖然存儲空間成本正在穩步降低,但越來越多的設備要麼製造時就是固定數量的存儲容量,要麼最終用戶難以擴展其存儲容量。

但當你的存儲空間不足時你可以做什麼呢?也許你會求助於雲存儲,或者你可以隨身攜帶一些外部存儲設備。

在這篇文章裏我會研究該問題的另一種解決方案:透明的文件系統壓縮,這是 Btrfs 的一個特性。理想情況下,這將解決你的存儲問題,同時幾乎不需要對你的系統進行修改!讓我們來看看是如何做到的。

透明壓縮的解釋

首先,讓我們來探尋 透明 壓縮是什麼意思。你可以通過像 gzip、xz 或者 bzip2 這些壓縮算法去壓縮文件。這通常是顯式操作:你利用一個壓縮工具並且讓它操作你的文件。雖然根據文件的內容,節約了空間,這有一個主要的缺點:當你想讀取文件或者修改的時候,你得先解壓縮。

這不僅是一個乏味的過程,而且也暫時打破了你之前節省的空間。再者,你最終解壓了你不想訪問的那部分文件內容。明顯有比這更好的方法!

相反,透明壓縮發生在文件系統級別。在這裏,壓縮的文件對用戶看起來像常規的未壓縮文件一樣。但是,它們是被壓縮後存儲在硬盤上的。這之所以可行,是因爲操作系統僅僅選擇性地訪問那部分文件,並且確保在向磁盤寫入更新時再次壓縮它們。

這裏的壓縮是透明的在於它不被用戶感知,除了在文件訪問時可能的 CPU 負載小量增加。因此,你可以應用在已有的系統而不是進行硬件修改或者求助於雲存儲。

壓縮算法對比

Btrfs 提供了多個壓縮算法的選擇。出於技術原因它不能選用任意的壓縮算法。它現在支持:

◈ zstd

◈ lzo

◈ zlib

好消息是,由於透明壓縮的工作原理,你不需要安裝這些程序供 Btrfs 使用。在下面的文章裏,你會看到如何去運行一個簡單的性能測試來對比壓縮算法。但是,爲了運行性能測試,你必須安裝必要的可執行文件。事後不需要留着它們,所以你將使用 Podman 容器來確保不會在系統中留下任何痕跡。

注意 :因爲 Btrfs 使用的壓縮依賴於內核對這些壓縮算法的(重新)實現,用戶空間版本的算法得出的結果應該認爲是粗略估計。

因爲一次次敲重複的命令是枯燥的工作,我已經在 Gitlab 上準備了一個可以運行的 Bash 腳本 (https://gitlab.com/hartang/btrfs-compression-test)。這會用上面提到的每個算法在不同的壓縮級別運行一次簡單的壓縮和解壓縮。

首先,下載腳本:

$ curl -LO https://gitlab.com/hartang/btrfs-compression-test/-/raw/main/btrfs_compression_test.sh

下一步,啓動一個 Fedora Linux 容器去掛載你當前的工作目錄,以便你可以和主機交換文件同時在那裏運行腳本:

$ podman run --rm -it --security-opt label=disable -v "$PWD:$PWD" \
    -w "$PWD" registry.fedoraproject.org/fedora:37

最後運行腳本:

$ chmod +x ./btrfs_compression_test.sh
$ ./btrfs_compression_test.sh

在我機器上的輸出是這樣:

[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!

重要的是在根據腳本得出的數據做決定之前注意這些事情:

◈ 不是所有的文件壓縮效果都一樣好。像圖片或電影這種已經壓縮過的現代多媒體格式不會壓縮得更小。

◈ 腳本中壓縮和解壓縮各進行一次。重複運行會產生稍微不同的輸出。因此,時間應該被理解爲是估計,而不是準確的測量。

鑑於輸出的數據,我決定在我的系統上使用壓縮級別 3 的 zstd 壓縮算法。依據你的需求,你可能想使用更高的壓縮級別(比如,如果你存儲設備相當的慢)。要估算可達到的讀 / 寫速度,可以將源存檔大小(約 260MB)除以(解)壓縮時間。

壓縮測試默認是對 GNU libc 2.36 源碼進行的。如果你想看看對指定文件的效果,你可以通過第一個參數傳遞文件路徑給腳本。記住文件一定要可以在容器內訪問纔行。

如果你想要測試其他東西或者執行更加詳細的測試,可以閱讀腳本的源碼,根據需要修改它。

配置 Btrfs 壓縮

Btrfs 裏的透明文件系統壓縮可以通過幾種方式配置:

◈ 作爲掛載文件系統的掛載選項(可用於相同 Btrfs 文件系統的所有子卷)

◈ 通過 Btrfs 文件屬性

◈ 在 btrfs filesystem defrag 時(不是永久的,不在這裏介紹)

◈ 通過 chattr 文件屬性接口(不在這裏介紹)

我只會介紹其中前兩個。

在掛載時開啓壓縮

有一個 Btrfs 掛載選項可以開啓文件壓縮:

$ sudo mount -o compress=<ALGORITHM>:<LEVEL> ...

例如,去掛載一個文件系統,並使用等級 3 的 ztsd 算法去壓縮,你可以寫成:

$ sudo mount -o compress=zstd:3 ...

設置壓縮等級是可選的。重要的是注意到 compress 掛載選項應用到整個 Btrfs 文件系統和它所有的子卷。此外,這是目前唯一支持的指定壓縮等級的方式。

爲了對文件系統的根應用壓縮,必須在 /etc/fstab 上指定。例如,Fedora Linux 安裝器,默認啓用級別 1 的 zstd 壓縮,在 /etc/fstab 裏是這樣:

$ cat /etc/fstab
[ ... ]
UUID=47b03671-39f1-43a7-b0a7-db733bfb47ff  /  btrfs   subvol=root,compress=zstd:1,[ ... ] 0 0

啓用單個文件壓縮

另外一種方式指定壓縮的方法是通過 Btrfs 文件系統屬性。使用下面的命令去查看文件、目錄或子卷的壓縮設置:

$ btrfs property get <PATH> compression

類似的,你可以像這樣配置壓縮:

$ sudo btrfs property set <PATH> compression <VALUE>

例如,對在 /etc 下所有文件啓用 zlib 壓縮:

$ sudo btrfs property set /etc compression zlib

你可以通過 man btrfs-property 得到支持值的列表。記住這個接口不允許指定壓縮級別。除此之外,如果設置了一個壓縮屬性,它會覆蓋掛載時的其他壓縮配置。

壓縮已有文件

在這時,如果你對現有文件系統採用壓縮,然後通過 df 或類似命令檢查空間利用率,你會發現什麼都沒變。這是因爲 Btrfs 自身不會 “重新壓縮” 所有已有的文件。壓縮只會發生在往磁盤寫新數據的時候。有一些方式去執行顯式的重壓縮:

1. 等待,什麼都不做:只要文件被修改並被寫回磁盤,Btrfs 根據配置壓縮新寫入的文件內容。如果我們等待足夠長,越來越多的文件被重寫,在某個時間點就會被壓縮。

2. 移動文件到另一個文件系統然後移動回來:取決於你想壓縮哪些文件,這可能是相當乏味的選項。

3. 執行一次 Btrfs 碎片整理。

最後一個選項可能是最方便的,但是它會對已經包含快照的 Btrfs 文件系統提出警告:它會破壞快照間的共享範圍。換句話來說,兩個快照間所有的共享內容,或者一個快照和它的父子卷,在碎片整理操作後將保存多份。

因此,如果你在你的文件系統裏已經有很多快照,你不應該對整個文件系統運行碎片整理。這也沒有必要,因爲如果你想的話,Btrfs 可以對特定的目錄或者單個文件進行碎片整理。

你可以使用以下命令去執行一次碎片整理:

$ sudo btrfs filesystem defragment -r /path/to/defragment

例如,你想像這樣去整理你主目錄的碎片:

$ sudo btrfs filesystem defragment -r "$HOME"

如果有疑問,最好從碎片整理單個大文件開始,並在監視文件系統上的可用空間的同時繼續處理越來越大的目錄。

測量文件系統壓縮

有時,你可能會想,文件系統壓縮爲你節省了多少空間。但如何判斷呢?首先,要知道一個 Btrfs 文件系統是否在掛載時啓用了壓縮,你可以使用以下命令:

$ findmnt -vno OPTIONS /path/to/mountpoint | grep compress

如果你得到了結果,那麼給定掛載點的文件系統就使用了壓縮!下一步,compsize 命令會告訴你你的文件需要多少空間:

$ sudo compsize -x /path/to/examine

在我的主目錄,結果是這樣:

$ 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

每一行告訴你應用到文件的壓縮 “類型” 。* TOTAL 是下面所有行的總計。

另一方面,這些列告訴你我們的文件需要多少空間:

◈ Disk Usage 是實際分配在硬盤上的空間,

◈ Uncompressed 是如果沒有壓縮,文件所需要的空間,

◈ Referenced 是所有未壓縮文件加起來的總大小。

Referenced 可以與數據 Uncompressed 不同,比如一個文件之前被重複了,或者有快照共享內容。在上面的例子,你可以看到在我的硬盤上總計 91 GB 的未壓縮文件僅佔據了 74 GB 的存儲。取決於在目錄裏存儲的文件類型和應用的壓縮等級,這些數字可以有很大差異。

文件壓縮的其它注意事項

Btrfs 使用啓發式算法去探測壓縮文件。這是因爲壓縮文件通常效果不好,所以沒有必要浪費 CPU 週期去嘗試進一步的壓縮。爲了這個目的,Btrfs 在寫入壓縮數據到磁盤之前測量壓縮率。如果文件的第一部分壓縮效果不好,文件被標記爲不可壓縮並且不會有後續的壓縮。

如果出於某些原因,你想 Btrfs 壓縮所有寫入的數據,你可以通過 compress-force 選項掛載一個 Btrfs 文件系統,像這樣:

$ sudo mount -o compress-force=zstd:3 ...

當像這樣配置,Btrfs 會用等級 3 的 zstd 算法壓縮所有寫入磁盤的數據。

一個重要的注意事項是掛載一個有很多數據並開啓壓縮的 Btrfs 文件系統會比沒開啓壓縮耗時更長。這是有技術上的原因的,而且這是一個不會影響文件系統操作的正常行爲。

總結

本文詳細介紹了 Btrfs 中的透明文件系統壓縮。這是一種內置的、相對廉價的方法,可以在不需要修改的情況下從現有硬件中獲得一些額外的存儲空間。

本系列文章的下一篇將討論:

◈ Qgroups - 限制文件系統大小

◈ RAID - 替換 mdadm 配置

(LCTT 譯註:後繼文章尚未發佈,一旦發佈我們會盡快翻譯。)

如果你想了解與 Btrfs 相關的其他主題,請查看 Btrfs 維基 [1] 和文檔 [2]。如果你還沒有閱讀本系列的前三篇文章,請不要忘記去看看!如果你覺得本文缺少某些內容,請在下面的評論中讓我知道。我們下篇文章見!

參考資料

1. https://btrfs.wiki.kernel.org/index.php/Main_Page ↩︎

2. https://btrfs.readthedocs.io/en/latest/Introduction.html ↩︎

(題圖:MJ/1a45064c-8da5-4b60-87f2-9886d6a3299e)


via: https://fedoramagazine.org/working-with-btrfs-compression/

作者:Andreas Hartmann 選題:lujun9972 譯者:A2ureStone 校對:wxy

本文由 LCTT 原創編譯,Linux 中國 榮譽推出

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/V09c9m-MbiCUNIGloQ_-Dg