资讯动态

Oans:btrfs/XFS块级去重工具原理与实战,回收磁盘空间

发布时间:2026/8/30 2:33:37 来源:尧图企业网站定制
Docker 镜像同步了好几份、备份轮转历史堆积了几十个目录、虚拟机快照越来越大——翻遍服务器却发现每一个文件“看起来都该留着”。这种空间焦虑经历过 Linux 存储运维的人应该都不陌生。真正的问题往往不是文件数量多而是同一份数据在磁盘上被物理复制了很多次。btrfs 和 XFS 用户此前想解决这个问题选择并不多老牌工具duperemove能做块级去重但大规模扫描耗时很长bees专注 btrfs对 XFS 用户帮不上忙而rmlint、jdupes这类工具更多是查找整个文件的重复项能处理“文件级重复”却处理不了“文件内部只有部分块重复”的情况。最近 Hacker News 上出现了一个新项目Oans定位非常直接btrfs 和 XFS 上的快速去重工具。它的核心思路不是帮你删除重复文件而是在文件系统底层把相同的数据块合并为一份引用。文件路径、目录结构、打开方式全部保持不变真实磁盘占用却会明显下降。这篇文章会讲清楚三件事Oans 这类工具背后的文件系统去重原理是什么在 btrfs 和 XFS 上怎么搭建测试环境、制造重复数据、跑通一次完整去重以及去重之后如何验证块共享确实发生有哪些坑需要避开。1. Oans 是什么不是又一次“重复文件清理”1.1 三种容易混淆的“去重”很多人一听到 deduplication第一反应是“找出重复文件然后删掉一个”。这个概念在文件系统层面并不准确至少可以分成三个层级维度文件级去重块级去重压缩比较粒度整个文件文件内部的数据块单个文件数据流是否透明可能改变文件链接关系对用户完全透明对用户完全透明典型工具/机制rmlint、jdupesduperemove、bees、Oansbtrfscompress选项典型场景完全相同的照片、文档容器镜像层、备份快照、相似虚拟机文本、日志、代码文件级去重通常把多个完全相同文件的 inode 合并改成硬链接。比如jdupes -L就是把重复文件替换为硬链接。问题是它只能识别“整个文件内容完全一致”的情况而且一旦某些程序按 inode 判断文件身份可能产生副作用。块级去重则是把文件拆成多个数据块计算哈希后找到内容相同的块让这些块在文件系统里共享同一个物理 extent。它不改变文件名、路径和 inode 语义只是底层数据块被复用了。这比文件级去重更彻底覆盖场景也更广。压缩和去重也不能混淆。压缩是在单个文件内部消除冗余模式对单份数据有效去重是在多个文件之间消除完全相同的数据块即使每个文件本身已经很小只要份数够多收益依然明显。Oans 做的事情是第三种块级去重。它调用的是文件系统内核接口而不是简单地在用户空间比较文件之后删除副本。1.2 Oans 与已有工具链的差异在 btrfs 和 XFS 生态里块级去重工具并不是空白市场。duperemove是老牌选择支持 btrfs 和 XFS原理是扫描文件、分块、计算哈希、找出相同块并调用去重接口。它的问题在于全量扫描时 CPU 和内存开销比较大目录一多等待时间会显著拉长。bees是 btrfs 场景下很受欢迎的后台去重守护进程思路是持续监控文件系统变化、在空闲时段做增量去重。但它基本绑定 btrfsXFS 用户没法直接使用。Oans 从项目定位上要解决的正是这两个痛点在 btrfs 和 XFS 上都提供可用的快速去重同时把扫描和匹配效率放在核心位置。具体使用 Rust、C 还是 Go 实现哈希算法选择什么是否支持增量扫描这些细节要以 Oans 项目仓库的 README 和源码为准但“面向多文件系统 更快”的方向是明确的。需要提醒的是Oans 从 Show HN 上出现意味着项目还比较年轻。新工具往往迭代快、API 不稳定生产环境引入前务必先看维护活跃度、License、issue 反馈和近期提交记录。1.3 为什么 btrfs 和 XFS 能成为“同一类工具”的目标块级去重不能脱离文件系统单独存在。内核里有一个名为FIDEDUPERANGE的 ioctl它允许用户程序告诉文件系统“这两个文件的这一段数据是相同的请让它们共享物理块。”不是所有文件系统都实现了这个接口。btrfs 天然是 CoW写时复制文件系统reflink 和共享 extent 是底层能力的一部分因此实现FIDEDUPERANGE水到渠成。XFS 长期以来以高性能和高扩展性著称传统上并不支持 reflink。从内核 4.9 开始XFS 逐步引入 reflink 支持配合新格式的 XFS 文件系统也能支持共享 extent 和去重操作。这就解释了为什么 Oans 能同时瞄准 btrfs 和 XFS它并不需要自己发明“共享块”机制只需要高效地完成“找相同块”这件事然后把结果交给内核去合并。而 ext4 没有 reflink 支持也没有实现完整的FIDEDUPERANGE所以这类工具天然无法覆盖 ext4。2. 核心原理块级去重是如何工作的2.1 CoW 与 reflink去重的地基要理解去重先理解 reflink。假设你有一个 1GB 的文件直接执行cp --reflinkauto original.bin copy.bin在支持 reflink 的文件系统上btrfs 和 XFS 会创建一个新的目录项同时让两个文件指向同一组数据块而不是真正复制 1GB 数据。磁盘占用几乎不变两个文件看起来都完整可读。关键差异在于写入行为当你想修改copy.bin时文件系统会先把将被覆盖的数据块复制一份再执行写入。这就是“写时复制”。原本共享的 extent 在修改处发生分裂其他未修改部分继续保持共享。去重工具做的事和cp --reflink方向相反。cp --reflink是在复制时直接建立共享去重则是在文件已经物理存在多份冗余之后扫描发现哪些块内容相同再用FIDEDUPERANGE把它们合并。因此去重本质上是一种“事后 reflink”。2.2 一次完整去重的四个阶段不管工具叫什么名字块级去重流程基本一致扫描文件系统或指定目录列出所有普通文件。将文件按一定策略切成数据块。计算每个块的哈希值用哈希表查找相同内容。对内容相同的块调用FIDEDUPERANGE合并并保留一份物理数据。分块策略很关键。如果整文件只算一个哈希那么需要两个文件完全一致才能去重如果按固定大小分块则能识别部分相同的数据但遇到文件内插入或删除数据导致块偏移时后续块全对齐不上。更高级的工具会采用内容定义分块CDC让块边界随内容变化代价是计算量更大。Oans 选择哪种分块策略直接决定它在大文件场景下的扫描速度和命中率。这也是技术选型时最值得研究的部分。2.3 FIDEDUPERANGE 的工作方式去重工具最终要通过系统调用把“哪些块需要合并”告诉内核。FIDEDUPERANGE接受一个源文件、一个目标文件以及两者各自的区间范围。内核会比较区间内的数据是否一致一致则建立共享 extent不一致则返回错误。这个过程涉及文件系统内部的 extents 管理。对用户空间工具来说它不需要关心块在磁盘上的具体物理位置只需要提供文件和偏移量内核会完成共享关系的建立。在实际工程中工具通常会把待去重的块按文件聚合尽量让同一个文件参与的 ioctl 调用次数更少、一次性合并更多区间从而减少系统调用开销。这也是“快速去重”和“慢速去重”在工程实现上的一个主要差异点。3. 适用场景与不适用场景3.1 适合用去重解决的场景备份归档目录。每天轮转的备份里大量未变化的数据块会反复出现块级去重能把历史备份的真实磁盘占用压到很低。容器镜像与构建缓存。不同镜像层之间、不同构建产物之间存在大量相同内容去重之后宿主机存储压力能明显缓解。虚拟机镜像库。多个虚拟机基于同一模板创建模板内容在一段时间内基本不变块级去重收益很高。测试环境快照。快照本身通过 CoW 共享已有数据但把快照导出成独立文件、或复制到其他目录后又会变成物理重复此时去重可以高效回收空间。3.2 不适合用去重解决的场景已经压缩或加密的数据。压缩后的数据本身熵很高文件之间重复块极少加密数据更是完全随机去重基本没有收益。频繁随机写入的在线数据库文件。去重后块共享关系很容易在下次写入时被打散反复去重反而增加碎片化和 CPU 消耗。SSD 容量极度紧张且要求低延迟的在线业务。去重会增加元数据复杂度和可能的碎片化对延迟敏感型业务不一定是好事。数据可靠性要求极高、但当前没有可靠备份的系统。任何批量修改文件系统元数据的操作都有风险没有备份就不应该跑。3.3 一个容易被忽略的判断去重的价值不在于“马上多出多少空闲空间”而在于它让“物理存储占用”和“逻辑数据量”解耦。归档类数据即使逻辑上保留了 10 份历史版本物理上也可能只占 1 份多一点。这一点对备份保留策略特别有价值因为你可以放心地把保留周期拉长而不必担心磁盘迅速被填满。4. 环境准备与安全前置4.1 在测试机上创建 btrfs 和 XFS 测试文件系统建议不要在正在使用的生产分区上直接实验。最简单的方式是创建两个 loop 镜像文件分别格式化为 btrfs 和 XFS 来测试。整个过程需要 root 权限请确认你操作的是测试环境。# 创建两个 2GB 镜像 truncate -s 2G /tmp/oans-btrfs.img truncate -s 2G /tmp/oans-xfs.img # 绑定 loop 设备 losetup -fP /tmp/oans-btrfs.img losetup -fP /tmp/oans-xfs.img # 查看 loop 设备名 losetup -l把输出中的/dev/loopX替换成实际设备名然后格式化mkfs.btrfs /dev/loop0 mkfs.xfs /dev/loop1 mkdir -p /mnt/oans-btrfs /mnt/oans-xfs mount /dev/loop0 /mnt/oans-btrfs mount /dev/loop1 /mnt/oans-xfs如果你的内核或系统版本较老XFS 可能尚未开启 reflink 支持。可以通过xfs_info查看文件系统特性确认输出中有reflink1。btrfs 自内核 3.x 起基本都支持 reflink 相关能力不需要额外配置。4.2 安装 OansOans 的具体安装方式以项目 README 为准。常见开源工具会提供预编译二进制也可以从源码构建。拿到工具包之后第一件事是执行帮助命令确认当前版本的参数形式oans --help如果输出里有--dry-run、--scan、--dedupe之类的选项建议先用--dry-run之类的模拟模式观察工具会做什么再真正去重。如果看到“usage”输出格式不同就以工具自身说明为准不要照搬其他工具的习惯。4.3 安全边界先备份再小范围验证去重操作会修改文件系统的 extent 共享关系。正常设计良好的工具不会丢数据但任何批量修改元数据的操作都有风险特别是当工具本身还处在早期迭代阶段时。运行前必须确认测试环境与生产环境隔离。有可用的备份或快照。有明确的回滚方案比如数据可以先复制到另一个磁盘。使用最小权限运行不要用 root 在非必要范围内全盘扫描。5. 完整示例制造重复数据并跑通一次去重5.1 制造典型重复场景先在 btrfs 测试分区里生成一个 512MB 的随机文件然后复制多份模拟备份目录里的重复文件cd /mnt/oans-btrfs # 生成原始数据 dd if/dev/urandom oforig.bin bs1M count512 # 模拟多个备份副本 for i in 1 2 3 4 5; do cp orig.bin backup-$i.bin done # 再模拟一部分只有局部重复的文件 cp orig.bin partial.bin dd if/dev/urandom ofpartial.bin bs1M count1 convnotrunc ls -lh执行后目录里会有orig.bin、backup-1.bin到backup-5.bin以及一个开头 1MB 被改写的partial.bin。前六个文件内容完全相同partial.bin有 511MB 与orig.bin相同。这是非常典型的归档场景多个历史版本之间大部分数据块相同只有少量差异。5.2 去重前记录空间占用用btrfs filesystem du来查看真实磁盘占用。这个命令会区分文件逻辑大小和实际独占的物理块大小。btrfs filesystem du --summarize /mnt/oans-btrfs df -h /mnt/oans-btrfs此时所有文件都是独立物理副本df应该显示接近 3GB 的占用512MB 原始文件加上 6 份 512MB 副本其中partial.bin也接近 512MB。5.3 运行去重命令在确认工具帮助信息后对指定目录执行扫描和去重。由于不同版本命令可能不同下面以常见的“扫描 → 去重”两步形态作为参考实际参数请以oans --help输出为准# 先扫描查看工具会识别出多少重复块如果有 dry-run 模式务必使用 oans --scan /mnt/oans-btrfs # 确认无误后执行去重 oans --dedupe /mnt/oans-btrfs如果工具支持只显示统计信息而不实际合并请优先执行这一步。观察输出的重复块数量、可节省空间和整体耗时再决定是否真正执行去重。6. 运行结果与效果验证6.1 用 btrfs filesystem du 验证共享去重完成之后再次执行btrfs filesystem du --summarize /mnt/oans-btrfs df -h /mnt/oans-btrfs预期结果文件逻辑大小没变但df显示的真实空间占用应显著下降。6 份 512MB 文件加 1 个 512MB 局部重复文件在理想情况下物理占用只会比“原始 512MB 少数差异块”略多。btrfs filesystem du输出的exclusive列尤其值得看它表示该文件独占的物理块大小。如果backup-1.bin的 exclusive 接近 0说明它几乎完全和其他文件共享物理块。6.2 用 filefrag 查看共享 extentfilefrag是 e2fsprogs 自带工具也可以用来查看文件物理 extent 分布filefrag -v /mnt/oans-btrfs/backup-1.bin filefrag -v /mnt/oans-btrfs/partial.bin共享 extent 通常会被标记为shared。如果看到大量 extent 带有 shared 标记说明块级去重确实生效了。这一输出在 btrfs 和 XFS 上都能提供参考。6.3 XFS 上如何验证在 XFS 测试分区上重复同样步骤可以采用类似命令filefrag -v /mnt/oans-xfs/backup-1.bin另外也可以使用xfs_io查看文件映射xfs_io -r -c fiemap -v /mnt/oans-xfs/backup-1.bin如果 extent 信息中显示被多个文件共享说明 XFS 的 reflink 去重已经生效。不同版本输出格式会有差异重点关注“共享”或“shared”相关标记。6.4 验证失败时的检查顺序如果跑完去重后df占用没有任何变化按以下顺序排查是否真的存在重复数据块可以用sha256sum orig.bin backup-1.bin确认文件内容一致。是否使用了正确的文件系统和挂载选项ext4 不支持这类去重。工具是否真正执行了去重还是只输出统计就退出了是否有其他进程打开着这些文件导致共享关系无法建立是否因为权限不足ioctl 调用返回失败表格汇总如下问题现象可能原因排查方式解决方案空间占用无变化文件内容本身不重复sha256sum对比使用真正重复的数据测试工具显示扫描到重复块但空间未释放文件被进程持续占用lsof查看打开句柄关闭进程后重试操作返回权限错误权限不足查看命令输出日志使用授权账号或 sudo 执行XFS 上去重无效果文件系统未启用 reflinkxfs_info查看特性重新创建支持 reflink 的 XFS 文件系统工具帮助命令不识别参数版本差异或依赖缺失查看项目 README 和--help按实际版本参数调整7. 常见问题与坑位分析7.1 去重后空间为什么没有立即释放块级去重建立的共享关系是文件系统内部的元数据变化不是删除操作。对于已经打开的旧文件句柄内核可能仍然保持对旧数据块的引用直到所有 fd 关闭。如果去重后空间没有立刻减少可以先等待一段时间或检查是否有进程正在读写相关目录。7.2 去重与文件碎片化去重会改变文件物理块的分布可能增加碎片化程度。对机械硬盘来说碎片化会影响顺序读取性能对 SSD 来说影响相对较小。高频更新的大文件不适合反复去重否则每次写入都会把共享块分裂出来去重带来的空间收益很快被元数据开销和碎片化抵消。7.3 去重与 btrfs 压缩的先后关系btrfs 支持透明压缩开启compresszstd后文件数据在写入时会被压缩。压缩后的数据和未压缩数据在去重时属于不同内容因此不要期待压缩和去重能直接叠加。更合理的策略是要么依赖压缩处理单文件冗余度要么依赖去重处理多文件之间的重复块根据实际数据特征选择适合的主手段。7.4 快照与去重的相互影响btrfs 子卷快照本质上就会共享数据块因此快照本身不是“需要去重”的场景。真正的问题通常出在把快照内容导出成独立文件、或通过其他方式复制到别处后重复数据又变回了物理独立。此时去重能帮助收回空间但要注意快照机制和去重机制同时存在时文件系统的共享关系会变得更复杂监控和排查也要更细致。7.5 数据安全与去重风险理论上FIDEDUPERANGE会先校验数据块内容一致再建立共享因此去重过程本身不应导致数据损坏。但工具存在 bug、内核版本存在缺陷、文件系统在去重过程中崩溃等小概率事件仍然存在。生产环境操作前必须验证备份可用并且最好先在测试分区完整跑一遍流程确认工具行为符合预期。8. 最佳实践与工程建议8.1 先文件级去重再块级去重对于明显是整文件重复的场景先用rmlint、jdupes这类文件级工具处理代价低、速度快。文件级去重完成后再用 Oans 做块级去重可以大幅减少需要哈希的数据量缩短整体耗时。这个顺序在归档类目录中尤其有效。8.2 控制扫描范围不要一上来就扫全盘去重工具的扫描范围和目录深度直接影响执行时间。建议按子卷或顶层目录逐个处理先从收益最高的备份目录、容器镜像目录开始观察扫描速度和空间回收比再决定是否扩大到更大范围。全盘扫描遇到活跃文件的可能性更高产生碎片的概率也更大。8.3 建立固定节奏而不是每天都跑归档数据通常是低频变化的。每月或每季度对归档目录做一次块级去重比每天全量扫描更合理。高频去重不仅增加系统负载还会把本该留给 CoW 机制的优化空间破坏掉。可以结合 crontab 在低峰时段运行并保留日志用于追踪空间变化。8.4 记录去重前、去重后的关键指标每次运行前记录以下指标方便判断工具是否真的带来了收益逻辑数据总量各文件大小之和。物理磁盘占用df或btrfs filesystem du。去重吞吐和耗时。文件系统碎片化情况。长期记录这些数据可以判断哪些目录适合去重、哪些目录去重后很快又膨胀从而不断优化策略。8.5 监控与告警如果 Oans 计划部署到准生产或生产环境建议在去重任务外部增加监控任务是否成功结束退出码是否正常。是否有FIDEDUPERANGE调用失败记录。去重前后空间变化是否符合预期。系统 I/O 和 CPU 在去重期间是否造成业务影响。出现异常时第一时间停止后续任务优先保证业务数据安全。8.6 对 Oans 项目的跟进建议新项目处在快速迭代期使用时锁定一个稳定版本不要每次发布都无脑升级。关注项目仓库的 issue 反馈、提交频率和维护者响应速度。如果发现问题优先在测试环境复现再到项目仓库提交清晰的复现步骤和文件系统版本信息。9. 总结与下一步实践Oans 这类工具解决的核心问题不是“帮忙删文件”而是让 btrfs 和 XFS 上本来物理重复的数据在文件系统层面变成共享块。它特别适合备份归档、容器镜像、虚拟机模板和测试快照这类静态数据居多的场景。而对于频繁写入的在线数据、已压缩文件、加密数据块级去重的收益很小甚至可能带来额外开销。如果手头正好有 btrfs 或 XFS 服务器建议先在一台测试机上用 loop 镜像搭一个独立文件系统复制几百 MB 到几 GB 的重复数据跑一遍 Oans 的扫描和去重再用btrfs filesystem du和filefrag -v对比前后变化亲自感受块级去重到底能回收多少空间。值得深入的方向还包括内容定义分块算法对命中率的影响、FIDEDUPERANGE在内核版本间的行为差异、btrfs 和 XFS 在共享 extent 管理上的异同。文件系统去重是个容易被低估的领域表面看只是“找相同块”实际做好扫描、哈希、合并和收敛需要相当多的工程细节。最后再强调一次安全底线任何去重操作都建立在对数据有备份、可回滚、先在测试环境验证的前提下。数据安全永远优先于空间收益。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价