群晖和飞牛 NAS 如何用 Btrfs + jdupes CoW 去重节省空间

介绍群晖和飞牛 NAS 上如何利用 Btrfs 的 CoW 能力,通过 jdupes 对重复文件做块级去重,节省空间并避免硬链接带来的修改风险。

如果 NAS 里经常保存影视文件、备份包、安装镜像、照片导出目录,重复文件很容易不知不觉占掉几百 GB 甚至几 TB。传统做法通常是删除重复文件,或者把重复文件改成硬链接,但这两种方式都不太适合 NAS 的长期存储。

更稳妥的方式是:在支持 CoW 的文件系统上,用 jdupes 做块级去重。

对群晖和飞牛这类 NAS 用户来说,如果存储卷使用的是 Btrfs,就可以利用 jdupes -B 调用文件系统底层能力,让重复文件共享同一份物理数据块。文件在目录里看起来仍然是独立文件,但底层只占一份空间。

CoW 去重和硬链接有什么区别

硬链接的逻辑是:多个路径指向同一个 inode。

这确实能省空间,但也有明显问题:如果某个程序修改了其中一个文件,其他硬链接路径看到的内容也会一起变化。对媒体库、同步目录、照片管理和备份目录来说,这种行为很容易埋坑。

Btrfs 的 CoW 去重不同。

它的逻辑是:多个独立文件可以共享相同的数据块;一旦其中一个文件被修改,文件系统会把被修改的部分写到新的数据块,其他文件不受影响。

也就是说,CoW 去重后:

  • 文件路径仍然互相独立;
  • 删除其中一个文件不会删除其他文件;
  • 修改其中一个文件不会改动其他文件;
  • 重复部分只在磁盘上占用一份空间。

这正是 jdupes -B 比硬链接去重更适合 NAS 的原因。

适合使用的场景

比较适合跑 CoW 去重的目录包括:

  • 影视库中重复下载的剧集和电影;
  • 不同目录里的安装包、ISO、压缩包;
  • 多次导出的照片或视频素材;
  • 备份软件产生的重复大文件;
  • 手工整理时复制出来的临时目录。

不太建议一上来就对整个系统卷运行。更稳的做法是先选一个用户数据目录,比如:

1
2
3
/volume1/video/
/volume1/photo/
/volume1/data1/aaa/

先小范围测试,确认命令、排除规则和节省效果都符合预期,再扩大范围。

基础命令

在 Btrfs 目录上使用 jdupes 做 CoW 去重,核心参数是 -B

1
jdupes -r -B "/volume1/data1/aaa/"

参数含义:

  • -r:递归扫描子目录;
  • -B:对找到的重复文件执行 Btrfs/CoW 去重,而不是创建硬链接;
  • "/volume1/data1/aaa/":要扫描的目标目录。

这里最关键的是 -B。它会让 jdupes 调用文件系统支持的去重能力,通常对应 reflink、clone 或 dedupe range 这类底层接口。

群晖建议排除 @eaDir 和 #recycle

在群晖上,不建议把 @eaDir#recycle 放进扫描范围。

@eaDir 是群晖系统生成的隐藏目录,里面通常保存缩略图、媒体索引、扩展属性和套件相关缓存。它数量多、文件小,扫描成本高,收益很低,还可能干扰群晖自己的索引服务。

#recycle 是共享文件夹的回收站。里面的文件本来就准备被清理,对它们去重意义不大,还会让结果变得难判断。

所以在群晖上,更推荐把这两个目录排除掉。

不同版本的 jdupes-X 参数支持不完全一样。有些教程会写成:

1
jdupes -r -B -X "req:*/@eaDir/*" -X "req:*/#recycle/*" "/volume1/data1/aaa/"

但部分群晖环境会报错:

1
Invalid extfilter filter name was specified

如果你的 jdupes -X 帮助信息里显示支持 nostr:text_string,可以使用更兼容的写法:

1
jdupes -r -B -X nostr:/@eaDir/ -X nostr:/#recycle/ "/volume1/data1/aaa/"

含义很直接:

  • -X nostr:/@eaDir/:排除路径中包含 /@eaDir/ 的文件;
  • -X nostr:/#recycle/:排除路径中包含 /#recycle/ 的文件。

如果目标路径包含中文或空格,一定要加双引号:

1
jdupes -r -B -X nostr:/@eaDir/ -X nostr:/#recycle/ "/volume1/photo/视频工作/"

飞牛 NAS 上怎么理解

飞牛 NAS 如果使用 Btrfs 存储池,思路和群晖类似:重点仍然是确认目标目录位于 Btrfs 文件系统,并使用 jdupes -B 做 CoW 去重。

区别在于系统隐藏目录名称可能不同。群晖常见的是 @eaDir#recycle,飞牛需要按实际目录结构决定排除规则。

建议先执行:

1
find "/你的目标目录" -maxdepth 2 -type d

看一下是否存在系统缓存、回收站、索引目录或应用数据目录。真正值得去重的通常是用户数据里的大文件,而不是系统生成的小缓存。

如何确认 CoW 去重成功

不要只看 ls -l。CoW 去重后,文件逻辑大小不会变,目录里看起来还是多个完整文件。

更可靠的验证方式有三种。

1. 看 jdupes 输出

运行过程中,如果看到类似输出,通常说明重复文件已经被识别并处理:

1
2
3
[SRC] /volume1/data1/aaa/1/file.mkv
====> /volume1/data1/aaa/2/file.mkv
====> /volume1/data1/aaa/3/file.mkv

[SRC] 表示源文件,====> 后面的文件表示被合并到同一份底层数据块上。文件仍然独立存在,但物理空间已经被压缩。

2. 对比 Btrfs 空间变化

在去重前后分别运行:

1
sudo btrfs filesystem usage /volume1/data1/aaa/

重点看这几个字段:

1
2
3
Used:
Free (estimated):
Data,single: Used:

如果去重生效,一般会看到:

  • Used 减少;
  • Data,single: Used 减少;
  • Free (estimated) 增加。

比如去重前 Used8.44TiB,去重后下降到 8.20TiB,哪怕只减少几十 GB,也说明 CoW 去重已经在释放物理空间。

3. 使用 compsize 查看真实占用

如果系统能安装 compsize,可以更直观看到 Btrfs 压缩和去重后的真实占用:

1
sudo compsize "/volume1/data1/aaa/"

它比普通 du 更适合理解 Btrfs 上的实际占用情况。

安全运行建议

建议按下面顺序操作:

  1. 先确认目标目录在 Btrfs 卷上;
  2. 先对一个小目录测试,不要直接扫整个卷;
  3. 排除系统隐藏目录、回收站和应用缓存目录;
  4. 路径包含中文或空格时使用双引号;
  5. 去重前记录一次 btrfs filesystem usage
  6. 去重后再次记录并对比 UsedFree (estimated)
  7. 不确定参数含义时,先运行 jdupes --help 看本机版本支持的过滤器。

最推荐的群晖命令可以从这一条开始:

1
jdupes -r -B -X nostr:/@eaDir/ -X nostr:/#recycle/ "/volume1/data1/aaa/"

如果你的 jdupes 版本不支持 nostr,就根据 jdupes -Xjdupes --help 的输出调整过滤参数。

小结

Btrfs + jdupes -B 的价值在于:不用删除文件,也不用把文件改成硬链接,就能让重复内容共享底层物理空间。

对群晖、飞牛这类 NAS 用户来说,它特别适合处理重复影视文件、备份文件和大体积素材目录。真正需要注意的是扫描范围和排除规则:优先处理用户数据,避开系统缓存和回收站,去重前后用 Btrfs 工具对比空间变化。

简单说,想安全省空间,优先记住这一句:

1
jdupes -r -B -X nostr:/@eaDir/ -X nostr:/#recycle/ "/你的Btrfs数据目录/"
记录并分享
使用 Hugo 构建
主题 StackJimmy 设计