Linux btrfs subvolume / snapshot 的 send / receive 增量备份流程

2次阅读

btrfs增量备份必须基于有祖先关系的快照,即后者须由前者(或其子快照)通过btrfs subvolume snapshot -r创建;否则send报“cannot find parent”错误。

Linux btrfs subvolume / snapshot 的 send / receive 增量备份流程

send/receive 增量备份必须基于两个有祖先关系的快照

不是任意两个快照都能做增量 btrfs send,必须满足「后者是前者的后代」——也就是后者是用 btrfs subvolume snapshot -r 从前者(或其子快照)创建出来的。否则会报错 Error: cannot find parent for ...

实操建议:

  • 每次备份前,用 btrfs subvolume list -t 查看快照的 Parent UUID 字段,确认继承
  • 推荐用命名规范强制可追溯,比如 @snap-2024-06-15-0200@snap-2024-06-16-0200,且后者一定从前者只读快照而来
  • 如果误删了中间快照,btrfs send -p 会直接失败,此时只能退回到全量 btrfs send,或用 btrfs send -c(需接收端已有相同父快照)

接收端 subvolume 必须不存在,且挂载点需支持 btrfs

btrfs receive 不会覆盖、合并或跳过已存在的子卷,只要目标路径下 @backup 已存在,就会卡住并报 ERROR: cannot create subvolume ... File exists。而且接收目录本身必须是 btrfs 文件系统挂载点——哪怕只是挂载了子卷,也不行。

实操建议:

  • 接收前先清理:btrfs subvolume delete /mnt/backup/@backup(如果存在)
  • 确保 /mnt/backup 是 btrfs 挂载点,而不是某个 btrfs 子卷的挂载;可用 findmnt -o SOURCE,TARGET,FSTYPE | grep backup 验证
  • 别把接收目录设成 /home 这类常驻挂载点——万一挂载选项不含 subvol=receive 可能写到根子卷,导致混乱

send 的 -p 和 -c 参数区别直接影响恢复灵活性

btrfs send -p 生成的是「相对父快照的增量流」,依赖发送端和接收端都持有那个父快照;btrfs send -c 则指定一个「已存在于接收端的快照」作为参考,允许你跳过中间快照做差量传输(比如从 v1 直接发 v3,只要 v1 在接收端存在)。

实操建议:

  • 日常轮转备份建议统一用 -p:逻辑清晰、校验简单,适合脚本自动化
  • 网络中断后重传或跨节点同步时,用 -c 更省带宽,但得先 rsyncscp 把父快照元数据同步过去(实际只需 subvolume list -t 能识别即可)
  • -c 不等于「任意快照都能当 base」:它要求该快照在接收端的 uuid 和发送端一致,即必须是同一个子卷的原始快照,不能是 clone 或 rename 后的副本

管道传输时务必加 --no-progresspv 控制节奏

默认 btrfs send 会输出进度行(如 At subvol ...),混在二进制流里会导致 btrfs receive 解析失败,报 ERROR: bad magic value。同时,未经节流的 send/receive 在慢速链路上容易因 buffer 满、超时断连。

实操建议:

  • 发送端一定要加 --no-progressbtrfs send --no-progress -p @snap-0615 @snap-0616 | ssh host 'btrfs receive /mnt/backup'
  • pv -s $(stat -c %s /path/to/snap) | 可视化进度,并通过 -L 限速避免打满带宽
  • 别用 gzip 管道压缩 send 流:虽然能减体积,但一旦出错无法定位是哪块数据损坏;如真要压缩,应在 send 前对源文件系统做 chattr +c 启用透明压缩

真正麻烦的不是命令敲错,而是快照之间隐含的父子关系断裂,或者接收端残留了同名但不同源的子卷——这些不会立刻报错,但某天 receive 突然失败,翻日志才发现 UUID 对不上。

text=ZqhQzanResources