TIME WAIT.
#Software Engineering 2026年8月3日 10 MIN READ

rsync 增量同步两个大目录:断点续传和完整校验

两个大目录不要塞进一条 rsync。先 dry-run,再串行增量复制,最后用二次比对和校验和确认——复制完成不等于可以删源盘。

rsync 增量同步两个大目录:断点续传和完整校验

迁两个大目录时,最容易踩的坑不是参数记不全,而是把两条路径随手塞进一条命令,或者看终端跳出 100% 就把源盘格了。

更稳的做法是:每组源和目标一一对应,先 --dry-run,正式跑时保留未完成文件并写日志,结束后再做只读比对。普通比对没输出,只说明按大小、修改时间和相关属性没有差异;要确认内容一致,再加 --checksum

本文关键参数和退出码对照过 rsync 手册 与上游退出码定义。

假设要做的是:

/mnt/disk1/photos/   → /mnt/disk2/photos/
/mnt/disk1/archive/  → /mnt/disk2/archive/

为什么不要写进一条命令

rsync 的最后一个位置参数是目标,前面的都是源。下面这种写法:

rsync -a /source/a/ /source/b/ /target/

是把两个源都汇进同一个 /target/,并不是 a → target/ab → target/b。两个目录要落到两个目标时,就跑两次,或者写串行脚本。

分开跑还有实际好处:日志和退出状态独立,一组失败不会把另一组一起报成成功;机械硬盘也能顺序读写,少抢磁头。如果两个源真要进同一目标,先看有没有同名相对路径——两边都有 2026/report.pdf 时,后跑的会盖掉先跑的。

先确认挂载点

大目录迁移里最危险的一种失败,是目标盘没挂上,路径却还在。rsync 会把数据写进根文件系统里的普通目录,直到系统盘写满。

开始前看设备和挂载:

lsblk -o NAME,SIZE,FSTYPE,UUID,MOUNTPOINTS
findmnt -T /mnt/disk1/photos
findmnt -T /mnt/disk2/photos
findmnt -T /mnt/disk1/archive
findmnt -T /mnt/disk2/archive
df -hT /mnt/disk1 /mnt/disk2
readlink -f "$SRC1" "$DST1" "$SRC2" "$DST2"

确认两块盘是预期设备、目标空间够、不是只读、不是同一文件系统上的两个名字、目标也不是源的子目录。再做一个小写入测试,只删自己刚建的测试文件,别用通配符清目标。

末尾斜杠决定复制层级

rsync -a /source/ /target/
rsync -a /source  /target/

第一条复制的是 /source/ 里面的内容;第二条会在目标下再造一层 source/。已经建好对应目录时,源和目标后面都写 /。第一次执行前用 --dry-run --itemize-changes 看将出现的相对路径,不要靠目录名猜。

可以直接用的命令

SRC1=/mnt/disk1/photos
DST1=/mnt/disk2/photos
SRC2=/mnt/disk1/archive
DST2=/mnt/disk2/archive

sudo mkdir -p "$DST1" "$DST2"

预演:

sudo rsync -aHAXn \
  --numeric-ids \
  --itemize-changes \
  "$SRC1/" \
  "$DST1/"

路径和变更列表都对了,去掉 -n 再正式跑。第一次迁移不要加 --delete:目标里多出来的文件先留着,结构和校验都确认后再决定要不要做成镜像。

sudo rsync -aHAX \
  --numeric-ids \
  --partial \
  --partial-dir=.rsync-partial \
  --info=progress2,stats2 \
  --log-file=/var/log/rsync-photos.log \
  "$SRC1/" \
  "$DST1/"

第二组同样处理,换路径和日志文件。两组串行,不要并行抢同一块机械盘。

这些参数各自管什么

基础组合是 -aHAX

参数作用注意
-a归档:递归并保留常见属性不含硬链接、ACL、扩展属性
-H保留硬链接大量硬链接会多占内存
-APOSIX ACL目标文件系统要支持 ACL
-X扩展属性目标要支持 xattr
--numeric-ids按数字保留 UID/GID适合 Linux 磁盘迁移
--partial中断后留下未传完的文件下次会接着检查
--partial-dir=.rsync-partial未完成文件放专用目录校验完再清
--info=progress2整个任务的总体进度--progress 更适合超大目录
--log-file写详细日志日志目录必须可写

-a 等价于 -rlptgoD,这点手册写得很清楚。目标是 FAT、exFAT 或某些网络盘时,权限、ACL、xattr 可能存不住,参数不是越多越安全。先看两端文件系统:

findmnt -no FSTYPE -T "$SRC1"
findmnt -no FSTYPE -T "$DST1"

普通照片、视频、文档如果本来就没有硬链接和 ACL,可以按实际减参数。

增量同步会复制什么

默认按大小和修改时间判断要不要更新。再跑同一条命令时:新文件会复制,变过的会更新,没变的跳过,目标独有文件会留下(没加 --delete 的话),中断的文件会按当前状态继续。

这不是文件系统快照。源目录一边拷一边写,目标可能拼进不同时间点的文件。照片库可以拷完再跑一次增量补齐;数据库、虚拟机镜像、正在写的应用数据,应先停写或打快照。

--itemize-changes 里常见几行:

预演里冒出意外的顶层目录名,多半是源路径末尾 / 写错。几乎所有大文件都要重传,就查时间精度、挂载参数,以及文件是不是还在被改。

长时间任务放进 tmux

数 TB 可能要跑很多小时。别指望 SSH 窗口一直开着:

tmux new -s rsync-copy
sudo /usr/local/sbin/sync-two-dirs.sh   # 或直接跑上面的 rsync
# Ctrl+b 再按 d 脱离
tmux attach -t rsync-copy

脚本里用 set -Eeuo pipefail,第一组非零就停,不要把整次任务报成成功。已经一致的文件重跑会被跳过,一般不需要另写一条「续传命令」。

结束先看 echo $?0 只表示这次运行没报错,不等于每个文件内容都核过。常见非零码(与上游 errcode.h 一致):

状态码含义
11文件 I/O 错误
12协议数据流错误
23部分文件未传完
24源文件在传输中消失
30发送/接收超时

日志里还有 IO errorPermission deniedfailedvanished file,就不能只看最后一行总字节数。

三层校验

第一层:正式复制后再 dry-run,并带上 --delete(仍有 -n,只报告不删除):

sudo rsync -aHAXn \
  --numeric-ids \
  --delete \
  --itemize-changes \
  "$SRC1/" \
  "$DST1/"

没输出通常表示:没有要新增或更新的文件,目标也没有源里没有的路径,要求保留的属性也对齐。只有 .rsync-partial/ 时,先确认没有未完成任务。出现 *deleting 先核对那些文件该不该留。

第二层:加 -c--checksum)。它会读双方内容再算校验和,用来决定要不要传,而不是只比大小和时间。带 -n 时只报告,不改文件。注意:这是 rsync 自己的传输判定校验,算法随版本协商(常见 MD5 / xxHash),不是 SHA-256 审计清单。数 TB 会把两块盘再读一遍,磁盘已经不健康时不要反复全盘扫。

第三层:比文件个数和逻辑字节数,用 find -xdev,别拿 du -sh 当唯一证据——稀疏文件、压缩、硬链接、块大小都会让占用对不上。

需要可留档的内容清单时,再在两端按相对路径做 SHA-256 再 diff。那只覆盖普通文件内容和路径,不管权限、ACL、硬链接和 xattr;完整 Linux 迁移要和 rsync -aHAXnc 一起看。

--delete 放到最后

--delete 会让目标变成源的镜像:源里没有的路径会被删。先只读预演,确认源目标没写反、挂载在线、每条 *deleting 都符合预期、目标没有要单独留的文件、另有一份可恢复备份。需要回看删除内容时,用 --backup --backup-dir=...,备份目录不要放进还会被同步的源树。

NAS 上常见 #recycle/@eaDir/.Trash-*/。排除 '#recycle/' 必须加引号,否则 shell 会把 # 当成注释。正式命令和校验命令要用同一套排除规则。

本地盘到本地盘通常不必 -z。照片、视频、压缩包再压一遍只浪费 CPU。生产机上可用 nice / ionice 降优先级;跨网络再考虑 --bwlimit(新版 rsync 支持 80M 这种后缀)。

常见翻车

什么时候才能删源

至少等到:两组 rsync 退出码为 0,日志没有未处理的 I/O / 权限 / 空间错误,普通 dry-run 没有意外输出,重要数据做过 -nc 或独立 SHA-256,关键文件能从目标打开。有条件的话再做一次从目标恢复的演练。

复制完成、校验通过、可恢复,是三件事。终端显示 100% 不等于源盘可以下线。

/related_artifacts

机械同理心:为芯片现实设计软件
#Software Engineering 2024年8月02日

机械同理心:为芯片现实设计软件

重新审视现代抽象层带来的性能损耗,并给出可操作的优化路径。

阅读全文 arrow_right_alt
2024 年的单体:有原则的简单性
#Software Engineering 2024年7月15日

2024 年的单体:有原则的简单性

解释为什么在许多团队里,单体架构比过早微服务更可持续。

阅读全文 arrow_right_alt
零成本抽象:深入 Borrow Checker
#Software Engineering 2024年5月12日

零成本抽象:深入 Borrow Checker

Rust 如何在没有 GC 的前提下保证内存安全,并保留可预测性能。

阅读全文 arrow_right_alt