主备两台服务器,要把主库产生的数据文件、用户上传的图片、配置文件同步到备机。最朴素的做法是 scp -r 或打个 tar 包传过去,但每天全量传一遍,几百 GB 的数据要跑几个小时,带宽和时间都扛不住。更大的问题是:同步脚本放到 crontab 里就以为万事大吉,直到某天切换时才发现备机的数据停留在三个月前——因为脚本早就报错退出了,而没有人看日志。rsync 是解决这类问题的标准工具,它只传输变化的部分,而且能告诉你"到底同步成功了没有"。本文从基本用法讲到定时任务的可靠设计,再到中断后的异常恢复。
一、rsync 为什么快:增量传输的原理
rsync 的核心不是"复制文件",而是"复制差异"。它的工作流程是:
- 源端和目标端先各自把文件按固定块大小切分,计算校验和(弱校验 rolling checksum + 强校验 md5 两类)。
- 目标端把自己的校验和列表发给源端。
- 源端比对,只把目标端没有的块和必要的匹配指令发过去。
- 目标端用本地已有的数据块 + 收到的新块重组出完整文件。
所以改一个 1GB 文件的中间几个字节,rsync 只传变化的那部分,而不是整个 1GB。这就是它比 scp 快几个数量级的原因。代价是两端都要消耗 CPU 做校验计算——在 CPU 很弱或文件极多时,CPU 可能先于带宽成为瓶颈。
判断文件是否需要同步的默认依据是文件大小 + 修改时间(mtime)。只要大小和 mtime 都一致就跳过。这带来一个重要推论:如果时间戳被改了但内容没变,rsync 会重新传;如果内容变了但时间戳没改(某些程序的行为),rsync 会漏掉。后者要用 --checksum(按内容校验和判断,慢得多)来解决。
二、核心参数速查
| 参数 | 作用 | 使用要点 |
|---|---|---|
-a(--archive) | 归档模式 | 等价于 -rlptgoD:递归 + 保留符号链接、权限、时间戳、属组、属主、设备文件。几乎每次都要加 |
-v / -vv | 详细输出 | -v 列出文件,-vv 显示跳过原因 |
-z | 传输时压缩 | 省带宽但耗 CPU。内网高速链路建议不用;跨公网、文本类数据推荐开启 |
--delete | 删除目标端多余文件 | 危险参数,让目标端与源端严格一致。必须先 --dry-run 验证 |
--exclude / --include | 排除/包含规则 | 支持通配符,可多次指定或用 --exclude-from=文件 |
--dry-run(-n) | 模拟执行,不实际改动 | 所有危险操作前的必做步骤 |
--bwlimit | 限速(KB/s) | 避免同步打满业务带宽,如 --bwlimit=10240 即约 10MB/s |
--partial / -P | 保留中断的部分文件 | -P = --partial --progress,大文件续传必备 |
--progress | 显示单文件进度 | 交互式看进度用,脚本里不推荐 |
--info=progress2 | 显示整体进度 | 脚本中比 --progress 更合适 |
-H | 保留硬链接 | 有硬链接的数据(如备份快照)必加 |
-A / -X | 保留 ACL / 扩展属性 | 需要 SELinux 上下文或 ACL 时加 -X |
--numeric-ids | 按数字 UID/GID 而非用户名 | 两端用户名不一致时防止属主错乱 |
--checksum(-c) | 按内容校验和判断差异 | 默认按 size+mtime;内容变了但时间没变的场景才用,慢 |
--link-dest | 硬链接到基准目录 | 做增量快照备份的核心参数 |
-e | 指定远程 shell | 如 -e 'ssh -p 2222 -i /root/.ssh/id_rsa' |
三、源目录结尾的斜杠:最经典的坑
这条必须单独讲,因为它是 rsync 误操作导致数据丢失的第一名原因。
rsync -av /data/ /backup/:把 /data 目录里面的内容同步到 /backup 里,结果是/backup/a.txt。rsync -av /data /backup/:把 /data 目录本身同步过去,结果是/backup/data/a.txt。
差别只有一个字符,结果完全不同。更危险的是配合 --delete:如果写成了 rsync -av --delete /data /backup/,目标端 /backup 里的其他文件会被判定为"多余"而删除,而 /backup/data 里多出来的那层目录又不在删除范围内。所以每次写命令前,先用 -n 干跑一遍,逐行看输出再执行。
# 危险动作前的标准流程:先干跑 rsync -avn --delete /data/ user@backup:/backup/data/ # 逐行确认输出里没有意料之外的 delete,再去掉 n 执行 rsync -av --delete /data/ user@backup:/backup/data/
四、同步实操:本地、远程 SSH、守护进程三种模式
4.1 本地同步
# 本地目录同步(同机备份、迁移到新盘) rsync -avHAX --numeric-ids /data/ /backup/data/ # 排除缓存与临时文件 rsync -av --exclude='*.tmp' --exclude='cache/' --exclude='.git/' /data/ /backup/data/ # 用规则文件管理排除项(规则多时更好维护) cat > /etc/rsync-exclude.txt <4.2 远程同步(SSH 模式,最常用)
# 配置免密 SSH(一次配置,长期生效) ssh-keygen -t rsa -b 4096 -C 'rsync-backup' ssh-copy-id -i ~/.ssh/id_rsa.pub backup@192.168.20.10 # 验证 ssh backup@192.168.20.10 hostname # 推送:本地 → 远程 rsync -avz --delete -e 'ssh -p 22' /data/ backup@192.168.20.10:/backup/data/ # 拉取:远程 → 本地 rsync -avz -e 'ssh -p 22' backup@192.168.20.10:/data/ /backup/data/ # 限速 + 整体进度 + 保留中断文件(生产脚本推荐组合) rsync -avz --delete --partial --info=progress2 --bwlimit=20480 \ --exclude-from=/etc/rsync-exclude.txt \ -e 'ssh -o StrictHostKeyChecking=no' \ /data/ backup@192.168.20.10:/backup/data/ # 只同步某些子目录(用 --include + --exclude 组合,注意顺序) rsync -avz --include='images/' --include='images/***' --exclude='*' \ /data/ backup@192.168.20.10:/backup/data/SSH 模式的优点是无需额外服务端配置、走成熟加密通道、权限控制清晰。缺点是每次要跑 SSH 握手,文件极多时开销明显。
4.3 rsyncd 守护进程模式(多客户端、高并发场景)
# 服务端配置 /etc/rsyncd.conf cat > /etc/rsyncd.conf </etc/rsyncd.secrets chmod 600 /etc/rsyncd.secrets # 创建运行用户与目录 useradd -M -s /sbin/nologin rsync mkdir -p /backup/data && chown -R rsync:rsync /backup/data # 启动(端口 873) rsync --daemon # 或用 systemd systemctl enable --now rsyncd # 防火墙 firewall-cmd --permanent --add-port=873/tcp && firewall-cmd --reload # 客户端用法(注意是双冒号 :: 模块名) rsync -avz --delete /data/ backup@192.168.20.10::data/ # 用密码文件避免交互 echo '你的密码' > /etc/rsync.pass && chmod 600 /etc/rsync.pass rsync -avz --delete --password-file=/etc/rsync.pass /data/ backup@192.168.20.10::data/ # 列出服务端可用模块 rsync --list-only 192.168.20.10::守护进程模式适合"很多台机器往一台备份服务器推数据"的场景:配置集中、可以做 IP 白名单、性能比 SSH 略好。缺点是传输默认不加密(跨公网要改用 SSH 模式或走隧道)。
五、定时同步:让脚本可靠而不是"看起来在跑"
把 rsync 丢进 crontab 是最容易出错的环节。常见问题有三个:上次没跑完下次又启动了(并发冲突)、失败了没人知道(静默退出)、SSH 免密在 cron 环境下失效(环境变量差异)。下面是可靠的脚本模板。
cat > /usr/local/bin/rsync-backup.sh <<'EOF' #!/bin/bash set -uo pipefail SRC="/data/" DST="backup@192.168.20.10:/backup/data/" LOG="/var/log/rsync-backup.log" LOCK="/var/run/rsync-backup.lock" EXCLUDE="/etc/rsync-exclude.txt" # 1. 加锁,防止上一次还没跑完就启动下一次 exec 9>"$LOCK" if ! flock -n 9; then echo "$(date '+%F %T') 上一次同步尚未结束,本次跳过" >> "$LOG" exit 0 fi # 2. cron 环境下显式指定环境,避免 ssh 找不到密钥 export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export HOME=/root echo "$(date '+%F %T') ===== 同步开始 =====" >> "$LOG" # 3. 执行同步,捕获退出码 rsync -az --delete --partial --numeric-ids \ --exclude-from="$EXCLUDE" \ --bwlimit=20480 \ -e "ssh -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no -o BatchMode=yes" \ "$SRC" "$DST" >> "$LOG" 2>&1 RC=$? # 4. 按退出码区分处理(rsync 的退出码有明确含义) if [ $RC -eq 0 ]; then echo "$(date '+%F %T') 同步成功" >> "$LOG" elif [ $RC -eq 24 ]; then echo "$(date '+%F %T') 警告:部分文件在传输中消失(源码变动),非致命" >> "$LOG" else echo "$(date '+%F %T') 同步失败,退出码 $RC" >> "$LOG" # 这里接告警:邮件/企业微信/钉钉 webhook echo "rsync 失败,退出码 $RC" | mail -s "rsync 同步失败 $(hostname)" ops@example.com fi echo "$(date '+%F %T') ===== 同步结束 rc=$RC =====" >> "$LOG" exit $RC EOF chmod +x /usr/local/bin/rsync-backup.sh # 加入 crontab:每天凌晨 2 点 crontab -l > /tmp/cron.bak echo '0 2 * * * /usr/local/bin/rsync-backup.sh' | crontab - crontab -l
rsync 退出码 含义 处理方式 0 成功 无需处理 23 部分文件传输错误(权限、磁盘满等) 需排查,检查日志中被跳过的文件 24 部分文件在传输过程中消失(源端正在变动) 通常非致命,重试即可;频繁出现说明源端变动过于剧烈 10 客户端报错(语法、参数问题) 检查命令与配置文件 11 无法打开/建立 socket 检查网络、rsyncd 是否启动、防火墙 12 rsyncd 认证失败 检查 secrets 文件内容与 600 权限 30 超时 调整 timeout,或检查网络质量 35 等待守护进程超时 检查服务端负载与 max connections 六、异常恢复:中断、误删与校验
6.1 传输中断后续传
# 加 --partial 保留已传部分,下次继续(大文件必备) rsync -avzP --partial /data/bigfile.tar.gz backup@192.168.20.10:/backup/ # 重新执行同一条命令即可续传,rsync 会复用 .xxx.partial 临时文件 # 如果上次没加 --partial,已中断的文件会遗留临时文件,先清理 find /backup -name '.*.??????' -type f -print6.2 误用了 --delete 导致目标端被清空
rsync 的删除是不可逆的,没有回收站。唯一可靠的事前防护是:
- 永远先跑
-n(dry-run)并把输出存档。- 重要目标端开启文件系统快照(LVM snapshot、ZFS/btrfs 快照、或云硬盘快照),同步前打快照。
- 用
--backup --backup-dir=/backup/deleted_$(date +%F)让被删除的文件先移到指定目录,而不是直接删除。# 安全版删除:被覆盖/删除的文件先转移到备份目录 rsync -avz --delete --backup --backup-dir=/backup/deleted_$(date +%F) \ /data/ backup@192.168.20.10:/backup/data/ # 用 LVM 快照做同步前的保护(ext4/XFS 均可) lvcreate -L 20G -s -n snap_backup /dev/vg_data/lv_backup # ...执行同步... # 确认无误后删除快照 lvremove -f /dev/vg_data/snap_backup6.3 同步完成后如何验证真的同步了
# 方法一:再跑一次 dry-run,无输出即表示已一致 rsync -avn --delete /data/ backup@192.168.20.10:/backup/data/ # 方法二:用校验和模式做深度比对(慢,但能发现内容不一致) rsync -avcn --delete /data/ backup@192.168.20.10:/backup/data/ # 方法三:对关键文件做哈希比对 cd /data && find . -type f -exec sha256sum {} \; | sort > /tmp/src.sha256 ssh backup@192.168.20.10 'cd /backup/data && find . -type f -exec sha256sum {} \; | sort' > /tmp/dst.sha256 diff /tmp/src.sha256 /tmp/dst.sha256 && echo "一致" # 方法四:统计文件数与总大小做粗校验 find /data -type f | wc -l du -sb /data小结与避坑建议
rsync 的核心价值在于"只传差异",但要让它真正可靠,靠的不是命令本身,而是围绕它建立的工程习惯:dry-run 先行、加锁防并发、退出码要判断、结果要校验、失败要告警。
避坑清单:
- 源目录结尾漏写或错写斜杠,配合
--delete后可能删错目录。写完先-n。- 不加
--dry-run就上--delete。这是 rsync 数据事故的头号原因。- 把
--delete用在备份场景。备份的目的是保留历史,源文件被删时备份里也应该保留一份。备份用--backup-dir或增量快照,不要直接--delete。- cron 里 SSH 免密失效。cron 的 HOME 与 PATH 与交互 shell 不同,脚本里显式
export HOME=/root并用-i指定密钥路径。- 不加锁。数据量大时一次同步可能超过 24 小时,下一次 cron 又启动,两个 rsync 同时写同一目标,结果不可预测。
- 不检查退出码。rsync 失败时通常不会"卡住",而是安静地退出,cron 也不会报错。脚本里必须捕获
$?并告警。- 日志不轮转。
-v模式日志增长很快,会写满磁盘。给 rsync 日志配 logrotate。- 跨公网用 rsyncd 明文传输。守护进程模式默认不加密,公网场景改用 SSH 模式。
- 忽略时区与时间偏差。rsync 依赖 mtime 判断,两端系统时间相差较大时会导致大量不必要的重传。用 NTP 同步时间。
- 以为同步等于备份。rsync 会把源端的误删"忠实"同步到目标端,
--delete更是如此。真正的备份需要保留历史版本(用--link-dest做快照式备份)。最后给一个快照式备份的思路:用
--link-dest可以做出"每个备份目录看起来都是完整全量,实际只占用增量空间"的效果,这是很多备份方案的基础。# 快照式增量备份:每天一个目录,未变化的文件硬链接到昨天 rsync -a --delete --link-dest=/backup/snapshots/2024-06-14 \ /data/ /backup/snapshots/2024-06-15/
本文为存储与硬盘管理系列,rsync 参数请以实际版本
rsync --version为准。
企业QQ咨询




