Linux 基础体系 · 第 50/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。
rsync 文件同步:增量算法、权限、删除、断点和备份策略
rsync 是一种面向文件和目录树的同步工具。它可以在本机目录之间同步,也可以通过 SSH 在两台主机之间同步。它最重要的特征不是“复制文件”,而是:
- 先比较源端和目标端的文件状态;
- 对已存在但部分内容变化的文件,尽量只传输变化的数据块;
- 按选项决定是否同步权限、所有者、时间戳、符号链接等元数据;
- 可选地删除目标端多余文件;
- 可选地保留被替换或删除的目标文件;
- 中断后再次运行时,重新比较并继续传输尚未完成的内容。
但 rsync 不是事务型复制工具,也不是数据库备份系统。它通常不提供整个目录树在同一时刻的一致快照,--delete 还可能造成真实的数据删除。因此,理解其数据流、状态变化和失败路径,比记住一条“万能命令”更重要。
一、先建立模型:源、目标、文件列表和同步方向
一次同步有两个角色:
- 源端(source):提供期望状态;
- 目标端(destination):被修改以接近源端。
例如:
rsync -a /srv/data/ /backup/data/
这里:
/srv/data/是源端;/backup/data/是目标端;- 目标端最终通常应包含源端目录树中的文件和元数据;
- 目标端原本多出来的文件不会自动删除,除非使用
--delete。
目录末尾的斜杠具有重要含义:
rsync -a /srv/data/ /backup/
复制的是 /srv/data/ 目录中的内容,结果通常是:
/backup/file1
/backup/subdir/file2
而:
rsync -a /srv/data /backup/
复制的是 data 这个目录本身,结果通常是:
/backup/data/file1
/backup/data/subdir/file2
可以将其概括为:
源目录/:同步目录内容;源目录:同步目录对象本身。
在生产环境中,目录末尾少写一个 /,可能导致备份出现在错误层级;在 --delete 场景下,还可能让操作者误判删除范围。
二、rsync 的增量算法究竟做了什么
2.1 “增量”不等于只比较文件名
对一个目标端已经存在的普通文件,rsync 通常不会简单地执行:
删除目标文件
重新发送整个源文件
而是尝试回答:
源文件中哪些内容可以由目标端已有的数据复用?哪些内容必须从源端传输?
设源文件为 ,目标端旧文件为 。目标端会将 划分为若干块:
其中:
- 是第 个数据块;
- 表示拼接;
- 块大小由实现和文件大小等因素决定,不应把某个默认块大小写死在脚本逻辑中。
目标端为每个块计算摘要,并将这些摘要发送给源端。源端扫描自己的文件 ,寻找能够复用的目标块;对于找不到匹配块的部分,则发送“字面数据”。
最终,源端向目标端发送的是一种描述:
复用目标端的 B3
发送字面数据 "new..."
复用目标端的 B4
发送字面数据 "..."
目标端根据这些指令重新构造新文件。
2.2 滚动校验和为什么有用
如果源文件在开头插入了一行,后面所有内容的绝对偏移都会变化:
旧文件:
AAAA
BBBB
CCCC
新文件:
XXXX
AAAA
BBBB
CCCC
如果只按固定偏移比较,AAAA、BBBB、CCCC 看起来都错位了,可能需要重新传输整个文件。
增量算法使用滚动校验和,使源端可以高效地尝试匹配滑动窗口。对窗口:
AAAA
计算摘要后,将窗口右移一个字节时,可以利用上一个窗口的部分计算结果,而不是从头重新计算整个窗口。这使源端能够在大文件中较快寻找旧块。
实际匹配通常包含两层判断:
- 弱校验和:计算快,用于筛选候选块;
- 强校验和:碰撞概率更低,用于确认候选块。
弱校验和发生碰撞并不意味着数据错误,因为还需要强校验和或后续校验来确认。具体摘要算法和可选算法会受到 rsync 协议版本、构建方式及参数影响,不能把某个固定摘要算法当作所有现代系统的永久保证。
2.3 一个完整的增量匹配算例
假设目标端旧文件按四字节分块:
目标端旧文件 T:
B1 = abcd
B2 = efgh
B3 = ijkl
B4 = mnop
源端新文件为:
源端新文件 S:
abcd
XXXX
ijkl
mnop
qrst
将其连续表示为:
abcdefghijklmnop # 旧文件
abcdXXXXijklmnopqrst # 新文件
源端扫描新文件时可能得到:
- 在偏移 0 处发现
abcd,匹配B1; - 接下来四字节是
XXXX,目标块中没有匹配,作为字面数据发送; - 接下来发现
ijkl,匹配B3; - 接下来发现
mnop,匹配B4; - 最后的
qrst没有对应目标块,作为字面数据发送。
传输逻辑可以写成:
COPY B1
LITERAL XXXX
COPY B3
COPY B4
LITERAL qrst
目标端据此生成:
abcdXXXXijklmnopqrst
这里有两个重要结论:
- 文件中间插入数据时,旧数据块仍可能被复用;
- “只传输变化部分”是一个尽量优化的结果,不是绝对保证。
如果文件很小、目标文件不存在、文件属性不适合增量处理,或者使用了 --whole-file,rsync 可能直接发送整个文件。
2.4 文件是否需要处理:快速检查与完整校验
默认情况下,rsync 通常先用文件的大小和修改时间判断文件是否需要更新。这种判断可表示为:
实际行为还受到时间精度、协议能力和相关选项影响。
这种方法速度快,但存在边界:
- 文件内容被修改后又恢复到相同大小;
- 修改时间被程序保留或手工伪造;
- 不同文件系统的时间戳精度不同;
- 源端和目标端的时间戳语义存在差异。
需要按内容校验时,可以使用:
rsync -a -c /srv/data/ /backup/data/
-c 或 --checksum 会读取文件内容并计算校验值,再决定文件是否需要更新。它会增加双方的磁盘读取和 CPU 开销,但它不等于“传输时关闭增量算法”。文件是否需要更新和更新时如何传输,是两个不同阶段:
--checksum:更严格地判断内容是否相同;- 增量传输:决定已变化文件如何减少网络传输。
如果网络很快、目标端文件完全不存在,或使用:
rsync -a --whole-file source/ destination/
则可以直接传输整个文件。--whole-file 会放弃块级增量传输,适合本机复制或网络带宽明显充足的情况,但不应机械地用于远程大文件同步。
三、从本机到远程主机:SSH、数据流和权限
典型的远程同步命令是:
rsync -a --info=progress2 /srv/data/ backup@example.net:/data/backup/
数据流大致如下:
sequenceDiagram
participant S as 源端 rsync
participant H as SSH
participant D as 目标端 rsync
S->>H: 建立 SSH 连接并启动远端 rsync
S->>D: 发送文件列表与元数据
D->>S: 返回目标端已有文件的块签名
S->>D: 发送字面数据和可复用块引用
D->>D: 写入临时文件并设置元数据
D->>D: 完成后重命名为目标文件
SSH 负责:
- 身份认证;
- 加密传输;
- 启动远端
rsync进程; - 传输 rsync 协议数据。
SSH 并不替代 rsync 的文件比较和增量算法。远端账号必须满足:
- 能读取源端对应数据,或能写入目标端对应目录;
- 远端存在兼容的
rsync; - SSH 账号的 shell、权限和路径配置允许启动远端程序。
例如,目标路径需要 root 权限时,可以这样指定远端 rsync 以 root 身份运行:
rsync -a \
--rsync-path='sudo -n /usr/bin/rsync' \
/srv/data/ backup@example.net:/var/backups/data/
前置条件是远端用户可以无密码执行指定的 sudo 命令。应使用精确的 sudoers 规则,而不是给备份账号开放任意 root shell。
四、-a 到底同步了什么
最常见的归档模式是:
rsync -a source/ destination/
-a 等价于:
-r -l -p -t -g -o -D
常见含义如下:
| 选项 | 含义 |
|---|---|
-r |
递归进入目录 |
-l |
保留符号链接本身 |
-p |
保留权限模式 |
-t |
保留修改时间 |
-g |
保留组 |
-o |
保留所有者 |
-D |
保留设备文件和特殊文件 |
-a 不是“完整复制一切”。它默认不包含:
- POSIX ACL;
- 扩展属性;
- 硬链接关系;
- 通常意义上的访问时间;
- 某些平台特有的创建时间或文件标志。
可按需要增加:
rsync -aHAX source/ destination/
其中:
-H:尽量保留硬链接关系;-A:保留 POSIX ACL;-X:保留扩展属性。
-HAX 可能显著增加文件列表内存和处理开销。对于包含大量硬链接、ACL 或 xattr 的系统树,必须在目标文件系统和挂载选项支持这些属性的前提下测试恢复。
4.1 所有者和组不是普通文件内容
如果目标端以普通用户运行:
rsync -a source/ destination/
即使命令没有报错,也未必能把目标文件所有者设置为源端所有者。常见结果是:
- 文件内容同步成功;
- 权限可能部分设置成功;
- 所有者保持为执行 rsync 的目标账号;
- 组只能设置为该账号有权使用的组。
需要跨系统保持 UID/GID 语义时,通常要考虑:
rsync -aHAX --numeric-ids source/ root@host:/destination/
--numeric-ids 表示按数字 UID/GID 处理,而不是依赖两端名称解析。它适合两端账号名称不同但数字 ID 具有既定含义的场景;如果两台机器的 UID/GID 规划不同,机械保留数字 ID 反而可能造成错误归属。
可以先用试运行检查结果:
rsync -aHAXnvi --numeric-ids source/ root@host:/destination/
这里:
-n:不真正修改;-v:显示详细信息;-i:显示变更项的 itemized 状态。
--dry-run 只能验证 rsync 计算出的操作计划,不能证明目标文件系统一定允许后续 chmod、chown、ACL 或 xattr 操作。
五、符号链接、硬链接、设备文件和特殊文件
5.1 符号链接
归档模式中的 -l 会复制符号链接本身,而不是默认跟随其指向内容:
ln -s /srv/data/current /backup-link
如果要跟随符号链接,需要明确使用相关选项,例如 -L。这可能改变同步边界:
- 符号链接指向目录外部时,可能把外部内容纳入同步;
- 出现循环或巨大目录树时,风险更高;
- 恢复时不再保留原来的链接语义。
因此,备份系统目录或发布目录时,应先确认是想保存“链接关系”,还是想保存“链接指向的数据”。
5.2 硬链接
两个硬链接共享同一个 inode:
ln original copy
只使用 -a 时,目标端可能得到两个内容相同但 inode 不同的普通文件。使用:
rsync -aH source/ destination/
才会尝试保留硬链接关系。
-H 的意义不只是节省空间,也可能影响程序行为。例如某些队列、邮件存储或去重目录依赖硬链接表示状态。另一方面,处理大量硬链接会增加内存和计算成本。
5.3 设备文件与特殊文件
-D 包括设备文件和特殊文件,但创建它们通常需要 root 权限,并且目标文件系统必须适合保存这些对象。普通用户备份 /dev,往往只能得到不完整或不具备恢复意义的结果。
这也是“文件级备份”与“块设备镜像”不同的原因:文件级 rsync 适合文件树,不会自动复制文件系统内部所有元数据、空闲块、引导结构或数据库页的一致快照。
六、删除:目标端多出来的文件何时会消失
默认同步是单向覆盖,不删除目标端多余文件:
rsync -a /srv/data/ /backup/data/
如果源端删除了:
/srv/data/old.log
目标端的:
/backup/data/old.log
仍然存在。
要让目标端更接近源端,可以使用:
rsync -a --delete /srv/data/ /backup/data/
这会删除目标端文件列表中被判定为多余的对象。
6.1 --delete 的逻辑边界
--delete 不是“删除目标目录中所有源端没有的东西”这么简单。它受以下因素共同影响:
- 源端实际生成的文件列表;
--exclude和--include规则;- 源端是否能正常读取目录;
- 删除时机选项;
- 目标端权限;
- 是否启用备份;
- 是否发生 I/O 错误。
被排除的目标文件通常不会因为普通 --delete 自动删除,因为它们不在同步文件列表中。--delete-excluded 会连排除项也纳入删除范围,风险明显更高:
rsync -a --delete --delete-excluded \
--exclude='*.tmp' \
/srv/data/ /backup/data/
这类命令不仅不复制 *.tmp,还可能删除目标端已有的 *.tmp。
6.2 删除时机
rsync 提供不同删除时机,例如:
--delete-before:传输前删除;--delete-during:传输过程中删除;--delete-delay:延迟收集删除操作,后续执行;--delete-after:传输后删除。
具体默认行为会受递归方式和 rsync 版本影响,因此脚本应明确指定所需语义,而不是依赖默认值。删除前执行:
rsync -a --delete --dry-run --itemize-changes \
/srv/data/ /backup/data/
重点检查输出中的删除项,例如:
*deleting old.log
--dry-run 不会删除文件,但它也不能保证正式执行时源端不会继续变化。
6.3 源端读取失败与误删除风险
假设源端某个目录因为挂载失败、权限变化或临时 I/O 错误而没有被完整读取。如果工具把这个不完整文件列表当作真实源状态,删除目标端对应文件就可能造成灾难。
rsync 对部分 I/O 错误有保护行为,但不能把这种保护当作备份策略。生产同步应:
- 先验证源路径确实已挂载;
- 检查源目录可读;
- 先执行
--dry-run; - 监控 rsync 退出码;
- 对删除启用备份目录或使用快照;
- 必要时使用
--ignore-errors前先理解它会削弱删除保护,不能随意添加。
可以限制一次最多删除的数量:
rsync -a --delete --max-delete=100 \
/srv/data/ /backup/data/
超过限制时命令会失败或停止删除;这适合作为异常变化的保险丝,但不是替代源端健康检查的方法。
七、断点和中断:rsync 如何继续未完成传输
7.1 普通重试本身就具有增量特性
假设大文件传输到一半时 SSH 断开:
rsync -a /srv/data/ backup@example.net:/backup/data/
默认情况下,未完成的临时文件可能在 rsync 退出时被删除。再次运行时,rsync 会重新传输整个目标文件,因为目标端没有一个可供继续使用的完整目标文件。
如果希望保留未完成文件,使用:
rsync -a --partial /srv/data/ backup@example.net:/backup/data/
--partial 保留部分传输文件。再次运行时,rsync 可能利用这份部分文件进行增量传输。
常见的进度写法是:
rsync -aP /srv/data/ backup@example.net:/backup/data/
-P 等价于:
--partial --progress
它适合人工执行,但在自动化脚本中最好明确写出选项,便于审计。
7.2 --partial-dir:把临时文件集中管理
可以指定部分文件目录:
rsync -a \
--partial-dir=.rsync-partial \
/srv/data/ backup@example.net:/backup/data/
这样未完成文件放在目标目录下的 .rsync-partial 中,而不是直接以最终文件名暴露。优点包括:
- 正常目录中较少出现半成品;
- 可以单独清理过期部分文件;
- 目标目录的业务程序较少误读临时文件。
该目录需要目标端账号可写。部分文件目录的权限和清理策略也必须纳入备份空间管理。
7.3 --append 和 --append-verify 不是通用断点续传
对于持续追加的日志或归档文件,可以使用:
rsync -a --append-verify source.log backup:/backup/
其前提是:
- 目标文件是源文件的前缀;
- 源文件只在末尾追加;
- 旧前缀没有被修改或截断。
--append 更偏向于信任目标端已有前缀;如果目标端文件曾损坏或源文件发生覆盖,可能保留错误数据。--append-verify 会在追加前后进行更严格的验证,代价是更多读取。
对普通会被随机修改的数据库文件、虚拟磁盘或压缩包,不应因为“想要断点”就使用 --append。这类文件不满足“目标是源的正确前缀”的条件。
7.4 --inplace 的一致性风险
默认情况下,rsync 通常先写临时文件,成功后再将其重命名为最终文件。这种方式的好处是读者不容易看到一个正在拼接的半成品。
rsync -a source/ destination/
--inplace 则直接修改目标文件:
rsync -a --inplace source/ destination/
它可能减少额外磁盘空间需求,也适合某些大文件场景,但风险是:
- 传输中断时,目标文件可能已经被部分改写;
- 业务程序可能读到混合的新旧内容;
- 与硬链接文件结合时,修改可能影响同 inode 的其他路径;
- 与备份、快照和稀疏文件行为结合时需要额外验证。
除非明确理解这些后果,否则不要把 --inplace 当作普通同步的性能开关。
八、备份策略:同步副本不等于备份
纯同步命令:
rsync -a --delete /srv/data/ /backup/data/
得到的是一个会随源端删除而删除的镜像。源文件误删、勒索软件加密或错误部署后,下一次同步可能把错误状态复制到目标端。
因此,需要区分:
- 镜像(mirror):目标尽量等于源;
- 备份(backup):保留过去的状态,以便回滚;
- 快照(snapshot):记录某个时间点的文件系统视图;
- 归档(archive):长期保存、通常不再频繁修改的副本。
8.1 使用 --backup 保留被替换或删除的文件
rsync -a --delete \
--backup \
--backup-dir=/backup/.deleted/2025-03-08 \
/srv/data/ /backup/data/
在目标端:
- 将被新版本替换的文件移入备份目录;
- 在
--delete下被删除的目标文件也可以移入备份目录; - 目标文件随后才按当前源端状态更新或删除。
--backup-dir 的路径解释与 rsync 版本及路径形式有关。使用绝对路径最容易审计;使用相对路径时,应明确它是相对于目标端对应目录解释,而不是相对于本地 shell 当前目录。
可以加上备份后缀:
rsync -a --backup \
--suffix='.'$(date +%F-%H%M%S) \
/srv/data/ /backup/data/
但仅靠时间后缀会带来目录膨胀,且同一秒内运行多次可能产生管理问题。更可靠的方案是由外部调度器创建唯一时间目录,并定义保留周期。
8.2 --backup-dir 的边界
它不是完整版本控制系统,也不自动提供:
- 目录树的原子快照;
- 去重;
- 加密;
- 异地复制;
- 保留周期清理;
- 恢复演练;
- 防止攻击者同时删除备份的隔离性。
如果目标备份目录长期可被源端服务账号写入,攻击者取得该账号后可能删除或篡改所有副本。因此生产备份通常还需要:
- 不同凭据;
- 备份端最小权限;
- 异地或离线副本;
- 文件系统快照或对象存储版本;
- 定期恢复测试。
九、用 --link-dest 制作节省空间的多版本目录
如果目标文件没有变化,可以使用硬链接让多个时间点共享 inode:
today=/backup/snapshots/2025-03-08
previous=/backup/snapshots/2025-03-07
mkdir -p "$today"
rsync -aH \
--link-dest="$previous" \
/srv/data/ "$today"/
如果 today 中某文件与 previous 中对应文件满足 rsync 的比较条件,目标可以创建硬链接,而不是重新复制数据。变化文件则写入新内容。
这形成类似:
snapshots/
├── 2025-03-07/
└── 2025-03-08/
删除一个快照目录中的硬链接,不会删除仍被其他快照引用的数据;只有最后一个硬链接被删除时,数据块才真正释放。
但 --link-dest 有几个边界:
- 它依赖目标文件系统支持硬链接;
- 源端和目标端必须有适合比较的属性;
- 若运行过程中源端变化,当前快照仍可能不是一致视图;
- 不能把硬链接快照当作不可变备份;
- 备份目录必须防止业务进程直接修改共享 inode 内容。
对数据库、虚拟机磁盘等不断写入的大文件,先创建 LVM、ZFS、Btrfs 或存储阵列快照,再对快照路径执行 rsync,通常比直接读取活动文件更容易得到一致状态。但快照能力取决于文件系统、卷管理器和应用本身,rsync 不会自动创建这些快照。
十、排除规则:同步边界必须明确
例如只同步源码和配置,不同步构建产物:
rsync -a \
--exclude='.git/' \
--exclude='build/' \
--exclude='*.tmp' \
/srv/project/ /backup/project/
排除规则不仅决定“复制什么”,还影响:
- 文件列表;
--delete的删除范围;--backup能否接管被删除对象;- 目录是否继续递归;
- 末尾斜杠和模式匹配位置。
执行前应使用:
rsync -a -nvi \
--delete \
--exclude='.git/' \
--exclude='build/' \
/srv/project/ /backup/project/
不要只看“将复制哪些文件”,还要检查:
*deleting行;- 被排除的目录是否导致预期文件消失;
- 规则是否匹配了比预期更大的范围;
- 源端路径是否正确。
当规则较多时,放入文件并显式指定:
rsync -a \
--exclude-from=/etc/rsync/project.exclude \
/srv/project/ /backup/project/
排除规则文件应纳入配置管理,否则未来恢复时可能无法解释某些文件为何从未进入备份。
十一、不要把 rsync 当作一致性快照工具
rsync 会遍历目录、读取文件并传输数据。源端文件在这个过程中继续变化时,可能出现:
1. rsync 先读取文件前半部分;
2. 应用修改文件后半部分;
3. rsync 再读取文件后半部分;
4. 目标端得到一个从未真实存在过的混合版本。
对于静态文件,这通常影响较小;对于以下对象则风险明显:
- SQLite 数据库;
- 正在写入的日志;
- 虚拟机磁盘;
- 邮件队列;
- 应用上传中的大文件;
- 多文件之间需要事务一致性的目录。
解决路径不是增加 -c 就够了,因为 -c 主要改变比较方式,不能让多个文件在同一时间被冻结。可采用:
- 让应用执行一致性导出;
- 停止或暂停写入;
- 对底层卷创建快照;
- 从快照目录执行 rsync;
- 恢复后进行应用级校验。
例如,数据库备份应优先使用数据库提供的 dump 或在线备份机制,再用 rsync 同步 dump 文件,而不是直接复制活动数据库文件。
十二、错误处理、退出码和验证
rsync 的命令输出不是唯一的成功标准。自动化脚本至少要检查退出码:
set -u
if rsync -a --delete --backup \
--backup-dir=/backup/.deleted/"$(date +%F-%H%M%S)" \
/srv/data/ /backup/data/
then
echo "sync succeeded"
else
status=$?
echo "sync failed, exit code=$status" >&2
exit "$status"
fi
常见错误类型包括:
- SSH 认证失败;
- 远程 rsync 不存在;
- 目标目录无写权限;
- 磁盘空间不足;
- 文件系统不支持所需属性;
- 源端路径未挂载;
- 传输中断;
- 某些文件读取失败;
- 文件名或 shell 参数引用错误。
rsync 的退出码可能表示成功、部分成功、语法错误、连接失败或其他错误;不同版本的具体代码表应以目标系统的 man rsync 为准。不要把“输出中出现了某几行”作为脚本成功判据。
12.1 查看计划而不修改目标
rsync -aHAXnvi \
--delete \
/srv/data/ /backup/data/
重点:
-n:只计算计划;-i:显示属性变化;-v:增加可读信息。
正式执行前,可先验证目标挂载点:
mountpoint -q /backup || {
echo "/backup is not mounted" >&2
exit 1
}
这是防止“备份盘未挂载,rsync 却把数据写到根文件系统普通目录”的关键检查。若随后使用 --delete,错误挂载点还可能导致更严重的错误操作。
12.2 同步后验证内容和元数据
针对内容:
rsync -aHAXnc \
/srv/data/ /backup/data/
如果没有需要更新的文件,说明按当前比较规则双方一致。-c 会读取两端文件内容,耗时可能较长。
针对抽样校验:
sha256sum /srv/data/example.iso
sha256sum /backup/data/example.iso
针对权限、所有者和链接:
stat /srv/data/example.iso
stat /backup/data/example.iso
getfacl /srv/data/example.iso
getfattr -d /srv/data/example.iso
不同系统未必安装 getfacl 和 getfattr;命令不可用不等于对象不存在,必须先安装相应工具或使用系统提供的属性检查方式。
十三、权限和删除结合时的典型失败路径
场景一:普通用户运行归档模式
rsync -a /srv/data/ /backup/data/
可能发生:
- 文件内容复制成功;
- 时间戳设置成功;
- 某些权限设置成功;
- 所有者和特殊文件无法恢复;
- rsync 返回非零退出码或输出权限错误。
如果脚本只检查目标文件是否存在,就会误判为成功备份。
场景二:目标目录权限过严
目标文件存在,但 rsync 无法覆盖或重命名:
permission denied
failed to set times
unable to delete
默认临时文件加重命名的流程还需要目标目录本身可写,不仅要求目标文件可写。即使目标文件权限允许写入,如果目录不允许创建或重命名,也可能失败。
场景三:源端目录突然消失
例如 /srv/data 原本是挂载点,备份前挂载失败,实际变成一个空目录。若直接执行:
rsync -a --delete /srv/data/ /backup/data/
可能得到一个“看起来成功”的空源同步计划,并删除目标端大量文件。挂载检查、--dry-run、删除备份目录和快照是针对该风险的不同防线。
十四、硬链接、稀疏文件和大文件的取舍
14.1 稀疏文件
稀疏文件的逻辑大小很大,但中间可能包含大量未分配的零块。同步时可考虑:
rsync -aS source/ destination/
-S 或 --sparse 尝试在目标端保留稀疏结构。它适合虚拟磁盘等文件,但必须验证目标文件系统和后续程序行为。
逻辑大小相同不代表物理占用相同:
stat --format='size=%s blocks=%b' image.raw
这里 size 是逻辑字节数,blocks 是实际分配块数的系统报告值。备份容量规划不能只看逻辑大小。
14.2 大文件与带宽
大文件发生少量中间修改时,块级算法可能节省网络流量,但仍需要:
- 读取源端文件;
- 读取目标端文件并生成签名;
- 进行块匹配;
- 写出新文件或原地更新。
因此,网络流量减少不代表磁盘 I/O 和 CPU 同样减少。对于本机快速存储之间的复制,--whole-file 有时更合适;对于高延迟或低带宽链路,增量算法更有价值。实际选择应通过测试和监控决定,而不是凭参数名称推断性能。
十五、一个较安全的生产同步流程
下面的流程适合“源端目录同步到备份盘,并保留被删除文件”的场景:
第一步:确认目标挂载
mountpoint -q /backup
失败时立即退出,不执行 rsync。
第二步:确认源端不是空的错误挂载点
test -d /srv/data
findmnt -T /srv/data
如果 /srv/data 预期应是独立文件系统,检查 findmnt 的结果是否符合预期。
第三步:执行试运行
rsync -aHAXnvi --delete \
--backup \
--backup-dir=/backup/.deleted/2025-03-08 \
/srv/data/ /backup/data/
检查复制项、属性变化和删除项。
第四步:正式执行
mkdir -p /backup/.deleted/2025-03-08
rsync -aHAX \
--delete \
--backup \
--backup-dir=/backup/.deleted/2025-03-08 \
--partial-dir=.rsync-partial \
/srv/data/ /backup/data/
这里的作用是:
-aHAX:尽量保存目录树、硬链接、ACL 和扩展属性;--delete:删除目标端源端不存在的对象;--backup:删除或替换前先保留旧对象;--partial-dir:中断时保留部分传输状态。
第五步:检查退出码和日志
status=$?
if [ "$status" -ne 0 ]; then
echo "rsync failed: $status" >&2
exit "$status"
fi
实际脚本还应记录:
- 开始和结束时间;
- 源端和目标端挂载信息;
- rsync 完整输出;
- 删除数量;
- 目标剩余空间;
- 异常退出码。
第六步:定期恢复演练
从备份中恢复到隔离目录,而不是只检查“文件存在”:
mkdir -p /restore-test
rsync -aHAX /backup/data/ /restore-test/
然后验证:
- 文件内容;
- 权限和所有者;
- ACL、xattr;
- 符号链接;
- 硬链接关系;
- 应用是否能启动;
- 数据库是否能通过一致性检查。
没有恢复验证的同步任务,只证明“某些数据曾经写入目标端”,不能证明备份可用。
十六、常见误解和对应边界
误解一:-a 就是完整系统备份
不准确。-a 不自动包含 ACL、xattr 和硬链接,也不复制文件系统级元数据、引导信息、快照状态或数据库一致性状态。根据需求增加 -HAX,仍然不等于块设备镜像。
误解二:rsync 只传输变化的字节
这是目标而不是无条件保证。首次复制、目标文件不存在、使用 --whole-file 或算法无法有效复用时,可能传输整个文件。即使只传输变化数据,也可能读取双方完整文件。
误解三:再次运行一定能从中断位置续传
普通重试通常能避免重新传输已经匹配的完整块,但未完成临时文件默认可能被删除。要保留它,应使用 --partial 或 --partial-dir。--append 仅适用于严格追加模型。
误解四:--delete 只是清理垃圾
--delete 是破坏性操作。源端路径错误、挂载失败、排除规则错误或源端读取不完整,都可能导致目标端不应删除的文件被删除。
误解五:同步成功就代表备份成功
同步成功只表示该次 rsync 基本完成,不能证明:
- 源端数据当时是一致的;
- 元数据全部保存;
- 备份没有被篡改;
- 历史版本仍存在;
- 能够恢复并运行应用。
真正的备份策略需要版本、隔离、保留周期、监控和恢复演练共同组成。
十七、命令选择的最小决策表
| 目标 | 常用起点 | 关键风险 |
|---|---|---|
| 普通目录镜像 | rsync -a src/ dst/ |
不删除目标多余文件 |
| 严格镜像 | rsync -a --delete src/ dst/ |
删除错误路径中的数据 |
| 保存 ACL/xattr/硬链接 | rsync -aHAX src/ dst/ |
权限、文件系统和资源开销 |
| 中断后保留部分文件 | --partial 或 --partial-dir |
部分文件可能被误读 |
| 只追加的大文件 | --append-verify |
不适合随机修改文件 |
| 保留删除和替换版本 | --backup --backup-dir=... |
需要清理和隔离 |
| 多时间点节省空间 | --link-dest |
硬链接不是不可变快照 |
| 严格比较内容 | --checksum |
增加全量读取开销 |
| 预览计划 | --dry-run --itemize-changes |
不能预测源端后续变化 |
rsync 的核心能力可以概括为:用文件列表确定同步边界,用元数据规则决定对象属性,用块匹配减少传输,用删除和备份选项决定目标端历史如何处理。当它与一致性快照、应用级导出、权限设计、异地副本和恢复演练组合使用时,才适合承担可靠备份体系中的文件同步部分。
系列导航与关联阅读
- 系列入口:Linux 完整学习路线:从内核与文件系统到网络、性能和生产运维
- 上一篇:Linux 归档与压缩:tar、gzip、xz、zstd、校验和和大文件
- 下一篇:SSH 自动化与批量运维:Config、ProxyJump、ControlMaster 和密钥治理
- 延伸:Linux 备份与恢复:文件、块设备、快照、加密和恢复演练
官方资料
本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。

评论
0 条讨论