拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Oans:基于 reflink 的 btrfs 与 XFS 高速重复数据删除工具实践

这次我们来看一个比较硬核的 Linux 存储效率工具Oans。它是在 Hacker News 上以 Show HN 形式发布的开源项目核心定位是给btrfs和XFS文件系统提供高速重复数据删除能力。简单说如果你 NAS、备份盘、虚拟机镜像目录或容器镜像层里堆了大量重复数据Oans 这种工具能在不影响文件逻辑结构的前提下把物理占用空间压下来。先说几个大家最关心的点它不依赖 GPU纯粹跑在 CPU 和文件系统层面核心机制是 Linux 内核的 reflink 引用链接所以目标文件系统必须是 btrfs 或 XFS使用方式是命令行适合脚本化、定时化运行它的侧重点是“快”在扫描、哈希、合并重复块这条路线上做性能优化。下面我会先拆解去重原理再给出一套可以在测试环境完整跑通的功能验证流程、性能观察方法和排错清单。1. 核心能力速览能力项说明项目类型文件系统层重复数据删除工具来源Hacker News Show HN 形式发布的开源项目核心功能识别 btrfs 和 XFS 上的重复数据块通过 reflink 合并物理空间去重粒度块级去重不是按文件做整文件查重支持文件系统btrfs、XFS硬件需求不需要 GPU建议根据数据量预留足够 CPU 和内存具体需按实际版本测试启动方式命令行工具源码编译或按项目 README 安装后执行是否支持 API默认是 CLI 工具接口能力需参考项目文档是否支持批量任务可对目录或文件批量去重也可由 cron 或脚本调用具体参数参考项目文档支持平台Linux适合场景NAS 备份目录整理、虚拟机镜像去重、容器镜像层优化、归档存储整理需要注意的是表格里有些描述是基于“文件系统去重工具”这个定位做的合理推断。等你 clone 下项目后最准确的信息来源是仓库的 README 和当前版本的--help输出。任何去重工具都有一个共同前提生产数据必须先备份再操作这一点后面会反复强调。2. Oans 去重原理与设计思路2.1 文件系统去重与整文件查重的区别常见的查重工具比如 fdupes、rmlint原理是先比较文件内容哈希发现多个文件内容完全一致就保留一个物理副本其他文件替换成硬链接或者符号链接。Oans 这类文件系统去重工具不一样。它工作在内核的 reflink引用链接机制上。btrfs 和 XFS 都支持 reflink去重工具通过内核的FIDEDUPERANGEioctl 告诉文件系统“这两个文件的某些块内容一样你帮我把它们合并成同一份物理存储。” 文件系统会维护引用计数。文件在用户态看起来仍然是独立的应用完全感知不到变化但底层多个文件共享同一组数据块。这个机制有几个和普通硬链接完全不同的优点。第一不需要改变文件的目录项和 inode 结构所有应用照常读写。第二合并之后如果某个文件被修改文件系统会为修改的部分重新分配块不影响其他共享块的文件这也是最常见的写时复制行为。第三对 btrfs 快照和子卷非常友好因为它本身就是 Copy-on-Write 设计去重后的物理共享不会破坏快照链。2.2 扫描、哈希、比对、合并四步流程Oans 这类去重工具的处理流程通常可以拆成四步扫描遍历用户指定的目录或文件系统收集所有文件同时按文件大小分桶。文件大小都不一样的文件内容不可能相同可以直接排除。分块与哈希把文件按一定块大小切分对每一块内容计算哈希值。因为上一步已经按大小分桶哈希比对只需要在大小相同的文件之间进行计算量会小很多。内容确认对哈希值相同的块再做内容级确认防止哈希碰撞导致数据误合并。合并通过FIDEDUPERANGEioctl 让内核把重复物理块合并并释放多余空间。这个流程不需要整个文件完全一致。只要两个文件在某个偏移范围内的数据块相同就可以合并。所以在备份目录里那些既有相同片段又有差异的大文件依然能提取出大量可回收的重复块。2.3 全量扫描与增量扫描的取舍对一个几十 TB 的 NAS 目录每次全量扫描都意味着要读取海量文件数据开销不小。所以很多文件系统去重工具会逐步演进支持增量模式记录上一次扫描时的文件大小、mtime、ctime只处理发生过变化的文件。增量扫描的优点是快但前提是文件系统元数据没有被异常修改。如果目录长期归档、改动很少增量模式效率很高反过来如果文件被频繁重命名或移动增量记录可能失效或者漏掉某些本应参与去重的文件。这种情况下更稳妥的做法是周期性执行一次全量扫描。实际使用时你可以看 Oans 文档是否支持增量记录再看它的记录文件格式和工作原理。3. 适用场景与使用边界3.1 适合用 Oans 的场景备份目录整理用 rsync 或类似工具做的多版本备份里每次备份都有大量未变化文件Oans 可以把公共块合并显著降低存储占用。虚拟机镜像目录多台虚拟机基于同一个 base 镜像创建后镜像分散在不同目录块级去重能回收大量公共基础块同时保留每台虚拟机的独立磁盘文件后续写入没有影响。容器镜像层本地存放大量 OCI 镜像层时不同镜像之间往往有大量共享层用文件系统去重可以再压缩掉一层物理占用。日志与构建缓存CI 构建缓存、旧日志归档中包含大量重复文本块适合做定期去重。图片、视频素材归档同一素材的多个缩略图、转码版本、冗余备份之间的重复内容也可以在块级合并。3.2 不适合用 Oans 的场景频繁写入的数据库数据目录数据库文件内部结构变化频繁做了去重合并后可能很快被新写入破坏共享块去重收益很小还增加维护成本。已经大量使用快照的 btrfs 子卷btrfs 快照本身已经通过 CoW 技术共享物理块再跑一遍去重基本不会回收多少空间反而会增加大量 I/O 扫描成本。没有备份的数据盘去重操作不是零风险操作任何异常都可能导致数据问题。没有完整备份之前不要直接在生产数据上运行。inode 数量极大的目录文件数量达到百万级别时扫描阶段可能持续数小时先抽子目录做测试再逐步扩展范围。3.3 合规与安全边界Oans 处理的是本地文件系统不涉及网络请求或云端上传但这个工具的使用场景仍然有一些需要遵守的边界处理包含他人文件、版权素材或个人信息的数据目录前要确认数据来源合法对外分发去重结果时注意授权边界。如果文件位于加密卷或受保护数据环境中要确认解密后的逻辑块是否适合做去重。某些场景下块级去重可能带来信息泄漏风险比如多个用户共享同一份物理数据导致权限模型被绕过。在生产环境操作前务必先在测试文件系统上完整验证确认 Oans 在你当前内核和文件系统配置下的行为符合预期。4. 环境准备与前置条件4.1 操作系统与文件系统要求Oans 面向 Linux核心依赖是文件系统对 reflink 和FIDEDUPERANGE的支持。btrfs 对 reflink 去重的支持比较成熟从内核 4.x 开始即可用通常发行版默认编译选项都包含。XFS 需要较新内核版本。部分老版本 XFS 在格式化时没有开启 reflink 特性这类盘上无法使用去重。重新格式化并启用 reflink 特性之前确认数据已经迁移或备份。去重工具运行时如果目标分区不支持 reflink通常会直接报错或没有任何效果。先确认文件系统类型df -T /path/to/mount再确认 XFS 特性xfs_info /path/to/mount输出中能看到 reflink 相关字段才代表支持。4.2 内核与编译工具链Oans 的部署方式很可能需要从源码编译。先补齐基础工具链# Debian / Ubuntu sudo apt update sudo apt install build-essential git # RHEL / CentOS / Fedora sudo dnf groupinstall Development Tools sudo dnf install git检查内核版本uname -r对于现代发行版内核版本通常都能满足 reflink 需求。如果系统过旧需要先考虑升级内核或发行版。4.3 数据目录基线信息执行去重之前建议先采集目标目录的一组基线数据方便对比去重效果# 目录总大小和文件数量 du -sh /path/to/mount find /path/to/mount -type f | wc -l # 文件系统可用空间 df -h /path/to/mount如果文件数量很大先把目标缩小到一个子目录跑通流程后扩大范围。5. 安装部署与启动方式5.1 获取源码并编译下面命令是通用模板实际仓库地址、分支名、构建方式必须以 Oans 项目 README 为准。先克隆仓库并阅读文档git clone 项目仓库地址 cd oans cat README.md如果项目使用 Makefile 作为构建入口make sudo make install如果项目使用 CargoRustcargo build --release ./target/release/oans --help构建完成后直接查看帮助信息./oans --help这一步能确认二进制可运行也能确定当前版本实际支持的参数列表。不要照抄网上其他命令以你自己的--help输出为准。5.2 运行方式定位Oans 是没有常驻服务或 Web UI 的命令行工具。最普通的调用方式是对目录执行去重oans /path/to/target/dir正式执行之前看帮助信息中是否包含--dry-run、--preview、--verbose一类的选项。第一次运行务必使用只输出不执行的预览模式确认它计划合并哪些文件之后再做实际写入操作。如果项目不支持 dry-run就复制一份小目录到测试分区上验证不要直接跑生产目录。5.3 文件系统边界检查确认目标目录确实在 btrfs 或 XFS 分区上而不只是路径前缀相同。可以通过 bind mount 或软链接绕到其他文件系统的情况下工具会误判或报错先检查实际物理文件系统df -T /path/to/target/dir6. 功能测试与效果验证6.1 创建测试文件系统如果本机没有现成 btrfs 分区可以用一个普通文件模拟块设备并格式化为 btrfs这样不影响现有磁盘布局可以完整验证 Oans 效果。# 创建 2G 临时文件 dd if/dev/zero of~/test-btrfs.img bs1M count2048 # 格式化为 btrfs sudo mkfs.btrfs ~/test-btrfs.img # 挂载 sudo mkdir -p /mnt/test-btrfs sudo mount -o loop ~/test-btrfs.img /mnt/test-btrfs # 把目录权限给当前用户 sudo chown $USER:$USER /mnt/test-btrfsXFS 测试环境可以同样方式创建。如果当前内核和 mkfs.xfs 版本不支持默认开启 reflink需要手动加参数sudo mkfs.xfs -m reflink1 ~/test-xfs.img sudo mkdir -p /mnt/test-xfs sudo mount -o loop ~/test-xfs.img /mnt/test-xfs sudo chown $USER:$USER /mnt/test-xfs6.2 制造重复数据在测试文件系统里生成一批有大量重复块的文件。下面的例子会创建一个 20MB 的源文件、5 个完整物理副本以及一个前半部分相同、后半部分不同的文件。cd /mnt/test-btrfs # 创建随机数据源文件 dd if/dev/urandom ofsource.bin bs1M count20 # 复制 5 份物理副本 for i in 1 2 3 4 5; do cp --reflinknever source.bin copy-$i.bin done # 创建部分相同文件前 10M 相同后 10M 随机 head -c 10M source.bin partial.bin dd if/dev/urandom ofpartial.bin bs1M count10 seek10 convnotrunc这里cp --reflinknever确保复制出来的是真实物理副本而不是 reflink 共享块。如果使用默认的cp --reflinkautobtrfs 上可能直接生成共享块副本测试效果会变得不直观。查看空间基线df -h /mnt/test-btrfs正常情况下5 个 20MB 物理副本和一个 20MB 的部分相同文件物理占用大约 120MB 左右扣除元数据开销后可以用df和du确认。6.3 执行去重并验证空间回收进入去重步骤。先看帮助信息确认是否有 dry-run 参数有的话先跑预览oans --help # 如果支持预览模式 oans --dry-run /mnt/test-btrfs预览无异常后执行实际去重oans /mnt/test-btrfs执行完成后重新查看空间df -h /mnt/test-btrfs预期结果文件系统已用空间明显下降。5 个完全相同的 20MB 文件会合并成一份物理数据块占用接近 20MBpartial.bin 的前 10M 也会和源文件共享后面的随机部分单独保留。6.4 判断成功的标准按以下标准确认去重生效文件数量没有减少ls -l仍然能看到 5 个 copy 文件每个应用都可以正常打开。文件内容没有变化用 md5sum 校验所有文件内容和源文件保持一致。物理空间下降df -h中已用空间明显减小。命令退出码正常去重命令正常结束没有报错或异常中断。校验命令md5sum source.bin copy-*.bin partial.bin df -h /mnt/test-btrfs6.5 验证写时复制行为这是判断去重是否真正生效的关键测试。修改其中一个副本的首个字节然后观察文件系统空间变化printf \x01 | dd ofcopy-1.bin bs1 count1 convnotrunc df -h /mnt/test-btrfs预期行为copy-1.bin 被修改后文件系统为它的首个数据块分配了新物理块其他文件不受影响。md5sum copy-2.bin的内容应该完全不变。如果某个工具做的是硬链接修改一个文件会影响其他文件内容而 reflink 方式下文件仍然是逻辑独立的。6.6 测试过程的常见失败现象现象可能原因处理方式提示文件系统不支持去重分区不是 btrfs/XFS或 XFS 未开启 reflink检查文件系统类型和格式化参数空间没有下降没有可合并的重复块或块大小设置不合理先用测试文件验证基本功能运行时报权限错误当前用户对某些文件无读权限按需调整用户和目录权限扫描耗时过长文件数量过多或磁盘 I/O 带宽不足先小范围测试再分批执行7. 去重性能观察与调优参数7.1 Oans 的“快”体现在哪里项目标题强调 fast deduplication性能优化大概率集中在扫描方式、哈希算法、并发模型这几个方向。但到底快不快需要在自己的数据目录上跑一次才知道。建议观察四个维度。扫描耗时从启动到完成扫描的时间与文件数、总数据量、磁盘 I/O 速度强相关。SSD 和机械硬盘上的差异会非常明显。CPU 占用哈希计算阶段是 CPU 密集型如果跑在 NAS 或共享存储服务器上注意避免打满所有核心影响其他服务。可以用top或htop实时查看。内存占用如果工具把哈希表加载到内存文件越多内存占用越高。用/usr/bin/time -v记录进程最大驻留内存。I/O 占用扫描需要读取候选文件内容去重合并阶段也可能产生额外 I/O。用iostat -x 1观察磁盘繁忙程度。7.2 影响性能的关键参数去重工具通常提供一些参数具体选项名要以 Oans 的--help输出为准。常见的影响维度包括下面几项。块大小块越大哈希数量越少扫描更快但重复检测粒度变粗可能漏掉更小粒度重复块。块越小检测粒度更细空间回收更充分但哈希和 I/O 成本更高。默认值通常是性能和收益的折中先跑默认参数再看是否需要调整。哈希算法不同哈希算法速度差异很大比如 SHA-256 较慢xxHash 这类非加密哈希很快。对于本地去重场景非加密哈希通常够用但工具内部需要做内容级确认来防止碰撞。并发线程数多线程能加速哈希和扫描但会增加 CPU 压力。在共享存储或 NAS 上建议限制并发数避免影响同一台机器上的其他服务。最小文件大小阈值小文件数量多、去重收益小、扫描开销大。如果工具支持设置最小文件大小先跳过 4KB 或 16KB 以下的小文件是合理选择。7.3 调优建议工程上建议按下面顺序做第一轮测试使用默认参数记录扫描耗时、内存峰值、释放空间。如果内存峰值过高尝试增大块大小、限制并发线程数。如果空间回收比例明显低于预期尝试减小块大小但要评估扫描耗时增长是否可接受。如果分区上有大量小文件优先跳过小文件把时间留给大文件去重。保存每一轮的参数和结果形成自己的调优基线。8. 与常见去重工具的对比8.1 工具对比工具定位去重方式适用文件系统特点fdupes / jdupes通用重复文件查找整文件哈希比较可辅以硬链接任意文件系统适合整文件查重不做块级去重rmlint通用重复文件查找 去重文件级哈希支持 reflink支持 reflink 的文件系统功能丰富还有清理空文件等能力duperemovebtrfs 去重块级哈希 reflink主要面向 btrfsbtrfs 用户常用历史较久Oansbtrfs/XFS 去重块级哈希 reflinkbtrfs、XFS主打高速度具体能力以项目文档为准8.2 Oans 的定位分析从标题来看Oans 有两个明确卖点同时支持 btrfs 和 XFS覆盖面比只面向 btrfs 的 duperemove 更广。强调 fast说明在扫描或哈希阶段做了针对性优化。但“快”是需要数据支撑的。建议在同一个测试目录上用相同数据集分别跑 Oans 和 duperemove、rmlint记录扫描耗时、最大内存占用和最终释放空间。先有数据再谈快慢这才是更工程化的判断方式。9. 批量任务与脚本化运行9.1 分批处理策略大型目录不建议一次性全量扫描。一个更稳妥的做法是按子目录分批处理这样单个子目录失败不会影响其他任务# 按子目录执行具体参数以 Oans --help 为准 for dir in /mnt/data/backup/*/; do echo [$(date)] processing $dir oans $dir || echo failed: $dir done9.2 定时任务示例如果备份是每天凌晨完成的可以把去重放在备份之后。用 cron 或 systemd timer 都可以# 每天凌晨 2 点执行日志写入文件 0 2 * * * /usr/local/bin/oans /mnt/backup /var/log/oans.log 21第一次写定时任务前先手动运行一次确认 Oans 支持完全无交互运行不会卡在等待输入状态。9.3 日志、退出码与失败重试批量任务最怕静默失败。至少做三件事所有运行日志写入独立文件保留至少 7 天。命令执行后检查退出码失败时用邮件或 webhook 通知。如果任务中途崩溃确认 Oans 是否支持断点续跑不支持的话按子目录分批执行失败的小目录单独重跑。10. 常见问题与排查方法问题现象可能原因排查方式解决方案启动后提示文件系统不支持目标分区不是 btrfs/XFS或 XFS 未开启 reflinkdf -T查看文件系统类型xfs_info查看特征使用 Oans 前确认磁盘分区类型老 XFS 需要迁移数据后重建文件系统并开启 reflink运行提示磁盘空间不足文件系统元数据空间不足或目标盘已满df -i查看 inodebtrfs filesystem usage查看详情清理临时文件释放空间后再执行扫描阶段非常慢文件数量巨大或磁盘 I/O 瓶颈iostat观察磁盘利用率top查看进程分批处理调整块大小或并发线程数内存占用过高哈希表整体加载到内存/usr/bin/time -v查看 max resident size增大块大小分批处理去重后空间没有下降没有可合并的重复块或检测粒度太粗用测试文件验证功能确认数据分布调整块大小检查是否开启 dry-run 以外的实际执行模式修改某文件后其他文件内容变化工具可能用了硬链接而不是 reflink修改一个副本后校验其他副本哈希确认工具调用FIDEDUPERANGE而不是link()硬链接批量任务中途卡住工具等待输入或某个文件异常检查进程状态和日志确认工具支持非交互模式增加超时处理文件丢失或损坏去重过程中断电或异常退出检查 dmesg、fsck 状态所有去重操作前先做完整备份必要时先运行文件系统检查11. 最佳实践与使用建议11.1 去重前的备份策略这是最重要的一条没有之一。去重操作在异常情况下可能造成数据问题所以第一优先确认有完整备份。第二优先在测试文件系统上完整跑一遍流程。第三优先使用 dry-run 或预览模式确认工具意图。btrfs 用户可以用快照做临时保险# 去重前创建 btrfs 快照 sudo btrfs subvolume snapshot /mnt/data /mnt/snapshot-before-dedupe-$(date %Y%m%d)确认去重效果后再删除快照。XFS 没有原生快照必须依赖外部备份方案。11.2 分层去重策略不同数据目录的访问模式和变更频率差异很大建议分层处理热数据目录不做去重频繁写入会让去重收益在短时间内消失。温数据目录定期去重频率取决于数据变化速度比如每周一次。归档目录去重收益最大可以低频全量扫描比如每月一次。11.3 保留最小可运行测试集建议准备一份几十到几百 MB 的测试目录包含三部分内容几份完全相同的文件、部分内容相同的较大文件、一些零散小文件。每次调整 Oans 参数或升级版本后先在这个测试集上跑一遍确认没有回归问题再上生产。11.4 记录性能基准在生产目录执行任务前保留一份完整的基准数据echo before du -sh /mnt/data df -h /mnt/data # 执行去重 echo after du -sh /mnt/data df -h /mnt/data把每次运行的时间、参数、空间回收量记录下来长期来看能帮你判断什么时候该做全量扫描什么时候用增量模式更划算。12. 总结与下一步Oans 这类工具的价值在于它把去重粒度从文件级推进到块级并且同时覆盖 btrfs 和 XFS解决了传统查重工具找不到文件内部重复块的问题。如果你手里有大量备份、虚拟机镜像或容器镜像目录它值得你花半小时搭个测试环境验证。建议按这个顺序尝试先创建一个 btrfs 测试分区制造重复文件确认 Oans 能跑通并释放空间。再对真实目录用 dry-run 模式确认扫描范围注意它计划合并哪些文件。在备份完备的前提下对正式数据执行。记录全量扫描耗时和空间回收量再决定是否用定时任务运行。最容易踩的坑有两个一是 XFS 分区在格式化时没有开启 reflink导致去重没有效果二是没有 dry-run 就直接对生产目录执行带来不必要的数据风险。先备份、再测试、最后上量这个顺序在任何去重工具上都适用。如果 Oans 在你的数据集上表现不错后续可以把它接入备份脚本为不同数据目录配置不同的扫描频率并持续监控文件系统的写时复制行为是否正常。存储空间优化这件事收益最终要看实际数据跑一轮测试就知道值不值得用。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门