Btrfs

如何一次複製多個快照而不複製數據?

  • January 7, 2019

我有一個 3.7TiB 的實時 btrfs 文件系統,已滿 90%,包括舊快照和新的 4TB 備份硬碟。如何將所有現有快照複製到備份硬碟?

我試過

# btrfs send home_1 home_2 home_3 share_1 share_2 share_3 ...

但在傳輸 2/3 的快照之前備份硬碟已滿。所以我做了一些研究:

# ### create test image
# dd bs=1M count=1000 > btrfs_test.dd
# mkfs.btrfs btrfs_test.dd

# ### create snapshots
# btrfs subvol create testvol/
# btrfs subvol snapshot -r testvol/ testvol_0/
# ### (copy some ISO image)
# btrfs subvol snapshot -r testvol/ testvol_1/
# ### (proceed until testvol_3)

然後我的測試文件系統已滿 91%,使用了 818MiB。

# btrfs send testvol_* | wc -c 
At subvol testvol_0
At subvol testvol_1
At subvol testvol_2
At subvol testvol_3
1466441978     # 1398MiB >> 1000MiB

當簡單地用 1 個命令發送所有快照時,數據被複製,並且流的大小以及接收端的快照大小超過了原始使用的空間和硬碟的容量。

所以實際的問題變成了:如何在不複製包含在其中的 2 個或多個快照中的數據的情況下複製多個快照?

對於這個簡單的測試案例,我成功地嘗試了增量方法:

# ( btrfs send testvol_0; btrfs send -p testvol_0 testvol_1; btrfs send -p testvol_1 testvol_2; btrfs send -p testvol_2 testvol_3 ) | wc -c 
At subvol testvol_0
At subvol testvol_1
At subvol testvol_2
At subvol testvol_3
838778546    # 800 MiB < 1000MiB

但是在真實的文件系統上有多個子卷,每個子卷都有多個快照。我無法定義任何與 一起使用的順序-p。如果一個數據塊在子卷之間共享home_1,當然我只想傳輸和儲存一次home_2share_3任何想法如何做到這一點?

**TL;DR:**使用-c頂部描述的參數通常有效。當快照包含硬連結時,Linux 核心中有一個錯誤會在發送快照時觸發錯誤——有關詳細資訊,請參閱答案末尾的結論。


我正在試驗這個-c參數,它看起來很有希望:

# for i in {0..3}; do btrfs send testvol_$i $(ls -d testvol_* | head -n$i | sed 's/^/-c /'); done | wc -c
### btrfs send testvol_0
### btrfs send testvol_1 -c testvol_0
### btrfs send testvol_2 -c testvol_0 -c testvol_1
### btrfs send testvol_3 -c testvol_0 -c testvol_1 -c testvol_2
At subvol testvol_0
At subvol testvol_1
At subvol testvol_2
At subvol testvol_3
838778546    # also 800MiB

我仍然不確定這是否是我需要的。對此解決方案有何評論?

**更新:**為了使用我的真實文件系統進行測試,我編寫了一個 Perl 腳本來輕鬆發送一組子卷(創建列表-c很快變得乏味)並將一些數據發送到/dev/null

#!/usr/bin/env perl

use strict;
use warnings;

my @subvols = @ARGV
 or die "Usage: $0 SUBVOLUME ...\n";

for(@subvols) {
   -d $_
     or die "Not a directory: $_\n";
}

for my $i (0 .. $#subvols) {
   my $subvol = $subvols[$i];
   my @clones = map { ('-c', $_) } @subvols[ 0 .. $i-1 ];
   print "btrfs send $subvol @clones\n";
}

結果:

  • btrfs send some-subvolme_* | pv > /dev/null: 24GiB 0:04:17$$ 95.2MiB/s $$
  • perl btrfs-send-all.pl some-subvolume_* | bash | pv > /dev/null: 12.7GiB 0:03:58$$ 54.7MiB/s $$

這並沒有帶來多大的性能提升,但儲存空間減少了近 50%!我現在正試圖真正執行它……

**更新:**我成功傳輸了 2 個快照,但第三個btrfs receive失敗並顯示以下錯誤消息:

ERROR: unlink path/to/some/file/in/the/snapshot failed. No such file or directory

命名文件存在於subvol_2尚未subvol_3存在於.subvol_1

我試圖比較發送和接收的快照:

# ### sender
# du -s subvol_{1,2,3}
132472304       subvol_1
117069504       subvol_2
126015636       subvol_3

# ### receiver
# du -s subvol_*
132472304       subvol_1
117069504       subvol_2
132472304       subvol_3

前兩個快照似乎被正確傳輸,但是subvol_3subvol_1. 這絕對是一個複製,因為備份磁碟上的已用空間僅為快照總大小的 39% — 由於它們共享大多數文件,因此幾乎超過 1/3。

為什麼btrfs send subvol_3 -c subvol_1 -c subvol_2 | btrfs receive沒有正確傳輸快照但複製subvol_1然後無法刪除應該保留subvol_3且僅存在於中的文件subvol_2

**更新:**會不會是這個錯誤?https://patchwork.kernel.org/patch/10073969/

我正在使用核心 4.9 執行 Debian 9 Stretch,它似乎比更新檔更舊。

**更新:**因為找不到任何解決方案,我只是複制了每個子卷的最新快照。然後我有大約 500GiB 的可用空間並嘗試使用已複製的快照作為-p參數添加上一個快照。然後我收到了與上面相同的快照相同的錯誤消息。

**結論:**我得出結論,我遇到了上面連結的錯誤。我不得不使用更新的 Linux 核心重新啟動這台機器,或者從另一台電腦訪問文件系統,但這不可行,因為這是一個生產系統。

到目前為止,我對 btrfs 沒有任何問題,但是執行 rsnapshot(它會創建很多硬連結)並發送 btrfs 快照仍然可能在目前的 Debian 穩定版上出現問題。

引用自:https://unix.stackexchange.com/questions/492078