我有一个 btrfs 卷,我为其创建定期快照。快照会轮换,最旧的为一岁。因此,删除大文件实际上可能在删除后一年内无法释放空间。
大约一年前,我将分区复制到更大的驱动器,但仍保留旧分区。
现在新驱动器已损坏,因此取出数据的唯一方法是btrfs-restore
。据我所知,新驱动器上的数据应该仍然适合旧的、较小的驱动器,并且文件实际上并没有太大变化(最多添加一些新驱动器或删除一些新驱动器,但是一年的开销快照不应太大)。所以我决定将数据恢复到旧驱动器上。
然而,恢复的数据填满旧驱动器的速度比我预期的要快得多。我怀疑这与 btrfs 的实现有关:
- 创建一个大文件。
- 创建卷的快照。空间使用情况不会改变,因为两个文件(原始文件和快照中的文件)都引用磁盘上相同的范围作为其负载。然而,由于 btrfs 的写入时复制性质,修改这两个文件之一会增加空间使用量。
- 用相同的内容覆盖大文件。我怀疑空间使用量随着文件大小的增加而增加,因为 btrfs 没有意识到内容没有改变。结果,它复制文件占用的块,并最终用相同的内容填充它,在两组不同的块中创建两个相同的文件。
btrfs 是否提供了一种机制来通过查找“遗传相关”的文件(即通过复制文件和/或对它所在的子卷进行快照而从同一文件衍生)、内容相同但存储在单独的块集中的文件来恢复此情况,并将它们转回转发链接以便释放空间?
答案1
TL;DR:有一些工具可以完成此任务,但不是官方工具套件的一部分,也可能不在您的发行版存储库中。您必须从许多工具中进行选择,并且可能自己构建它。详情请参阅下文。
工具
btrfs wiki 有一篇文章关于重复数据删除,其中还提到了一些工具。
还有更多工具——我查看了其中一个,尽管在撰写本文时它似乎已经有 6 年没有维护了,所以我决定坚持使用 btrfs wiki 上的工具。
到目前为止,这些都不是官方 btrfs 套件的一部分,至少 Ubuntu 20.04 没有为它们提供软件包——你必须自己构建它们。
dduper
看起来很有希望——它声称可以进行基于文件和块的重复数据删除(即,它可以对整个文件以及两个或多个文件之间相同的块进行重复数据删除)。据说它也很快,因为它依赖于内部 btrfs 索引。由于它是用 python 编写的,因此不需要在使用前构建(prettytable
不过,您的计算机上确实需要 Python 包)。然而,它似乎会跳过任何低于 4 KB 的文件,我认为当您有大量相同的小文件时,这会适得其反。
我决定去duperemove
,它仅执行基于文件的重复数据删除。除了 C 构建环境和自动工具之外,您libsqlite3-dev
的计算机上还需要该软件包。获取源代码并通过make
从源目录运行来构建它们。duperemove
然后可以直接从源目录运行,对于那些不想make install
在系统上随机添加东西的人来说。
跑步duperemove
这文档提到两种运行方式duperemove
:直接运行,或者通过运行fdupes
并将其输出传输到duperemove
.第一个仅建议用于小数据集。然而,对于我的 2-3 TB 数据集和大约 400 万个文件来说,第二个结果非常消耗资源(一天后,进度约为 0.5%,并且内存使用量以及不断的交换导致系统性能下降)几乎无法使用)。
似乎对我有用的是
sudo duperemove -drA /foo --hashfile=/path/to/duperemove.hashfile
这会:
- 以 root 身份运行整个过程,因为我的帐户无权访问磁盘上的所有内容
- 对结果进行重复数据删除 (
-d
),而不是仅仅收集哈希值并输出重复的列表 - 递归到给定路径的子目录 (
-r
) - 以只读方式打开文件以进行重复数据删除(
-A
需要,因为我的快照是只读的) - 将哈希值存储在文件中,而不是内存中 (
--hashfile
),这给您带来两个优点:- 只要哈希文件保留在原处,该过程就可以随时中断并稍后恢复
fdupes
尽管哈希文件占用磁盘空间:每个块 90 字节,每个文件 270 字节,但该进程不会占用系统内存(与在模式下运行不同)。
duperemove
分多个阶段运行:
- 索引磁盘内容(即存储在哈希文件中的内容)
- 将重复的哈希加载到内存中
- 删除重复文件
duperemove
只要索引数据库保持不变,就可以随时中断和恢复。
检测结果
我运行duperemove
的磁盘有 8 个子卷,其中 5 个子卷定期创建快照,并保留 24 个快照(最近 12 个每月快照、5 个每周快照和 7 个每日快照)。包括快照在内,该磁盘可容纳约 5-600 万个文件,占用 3.5 TB(去重前,去重后预计为 1.8 TB)。
当duperemove
开始运行并显示进度时,该百分比仅指索引阶段,而不是整个重复数据删除过程。加载重复的哈希值可能比这要少得多或多得多,具体取决于检查了多少块。重复数据删除所花费的时间与索引的时间大致相同。此外,索引阶段的进度计算似乎仅基于文件数,而不是块数,如果所有大文件往往位于文件的开头(或结尾),那么它就成为所需总时间/空间的不可靠指标。该集。
索引阶段的资源使用量足够低,足以让我的系统在使用哈希文件时保持响应,尽管加载重复数据可能会占用您的可用内存。如果索引数据库大于系统上的可用内存量,则可能会导致过多的交换并降低系统速度。
对所有内容(默认块大小为 128K)进行索引大约需要 28 天才能完成,并生成了 21 GB 的哈希文件。我在第 36 天耗尽了内存,导致我的系统没有响应,所以我不得不中止。duperemove
四天来,该进程的内存使用量一直在 12-14 GB 左右波动,但总内存使用量不断增加,直到系统变得无法使用。
在接下来的尝试中,我决定逐一对子卷进行重复数据删除,并在我知道已移动数据的两个子卷的部分之间进行另一次运行。我从 1024K 块大小开始,尽管这会错过较小的重复块以及小于块大小的文件,以换取更好的性能。这花费了大约 24 小时,最终在我的驱动器上释放了大约 45 GB – 性能令人满意,但空间节省不是我所期望的。
我中止了在 300G 子卷上使用 4K 的另一次尝试 - 索引所需的时间大约是 1024K 的四倍,但 3 天后,加载重复哈希值仍然没有完成。另一次 64K 的尝试在 4 小时内完成。请注意,第一遍之后的任何后续传递的重复数据删除应该更快地完成,因为只有小块需要进行重复数据删除。
因此,根据实践经验,我的建议是:
- 哈希文件放置很重要:
- 确保您有足够的磁盘空间(我没有,所以我不得不中断,移动文件并恢复)。
/tmp
可能不是一个好主意,因为它可能会在重新启动时被擦除(即使您不打算重新启动,您可能希望在系统崩溃或断电时保持安全)。- 如果您已加密您的主目录,则性能将受到我所观察到的影响。根据我的经验,外部 USB 磁盘的性能似乎比内部 HD 上的加密主目录要好。
- 无法在两个(或更多)只读子卷之间对数据进行重复数据删除。如果您有同一子卷的多个只读快照,请选择路径,以便仅扫描这些快照之一 - 通常是最近的快照,或者您计划保留时间最长的快照。扫描所有快照将花费更长的时间,并且不会提高效率 - 除非当前集中的某些文件与较早的快照匹配,而不是最新的快照。
- 分部分重复数据删除——将文件系统分割成您不希望它们之间存在大量重复数据的部分。这将在加载重复哈希时节省空间。
- 确定最佳块大小,如中所述为 duperemove 选择正确的块大小。对于我的驱动器,16K 对于 0.6-1 TB(去重前)的部分效果很好,而在 8K 时,加载重复哈希值从几个小时增加到超过 2 天,这意味着 16K 是我一直在寻找的最佳选择。对于较小的部分,4K 效果很好。