1. 从一次凌晨的备份失败说起
很多年前,我第一次在生产环境做数据迁移,用的还是scp。当时同步一个项目目录,几十个G的数据,scp 吭哧吭哧跑了快两个小时,中途网络抖了一下,整个传输直接失败,又得从头再来。那时候我就在想,如果有一个工具能支持断点续传、增量同步、两端数据校验,该多省心。后来接触到 rsync,才发现自己之前一直在用锤子拧螺丝。
rsync(remote sync)是一款开源的文件同步工具,几乎所有的 Linux 发行版都默认自带。它的核心能力可以用三句话概括:增量传输、断点续传、两端文件状态对比。这些能力让它成为服务器备份、代码发布、数据迁移、镜像同步等场景下的首选工具。
这篇内容适合谁看?如果你正在维护一台或几台 Linux 服务器,需要定期把数据从一台机器同步到另一台机器;或者你在做 CI/CD 发布流程,需要把构建产物分发到多台服务器;又或者你需要一份可靠的远程备份方案,rsync 都值得你花半小时彻底搞懂它的用法和原理。
这篇博文我会从为什么选 rsync 说起,再深入到它的核心同步机制,然后给出完整的实操步骤、常用参数、增量备份方案,以及我在实际运维中踩过的一些坑。相信我,看完之后你会想把手头所有 scp 脚本都改成 rsync。
2. 为什么选择 rsync:对比 scp、cp、tar 管道同步
很多人会问:既然 scp 也能远程拷贝文件,tar 加管道也能打包传输,为什么非得用 rsync?这个问题问得好,直接决定了你后面会不会主动用它。
2.1 scp 和 rsync 的本质区别
scp 的同步逻辑是“全量拷贝”,不管你目标位置是否已经有相同文件,它都会完整传一遍。文件多了、大了,传输时间线性增长,而且一次中断就得重来。rsync 则不同,它会在传输前先对比源和目标的文件列表,只有那些大小、时间戳、校验值有差异的文件才会被传输。
打个比方:你有一个 100 本的书架,要搬到隔壁房间。scp 的做法是把 100 本书全部搬过去,哪怕对面已经有 99 本一模一样的。rsync 的做法是先看一眼对面缺哪几本,只把缺失的那 1 本搬过去。效率差距一目了然。
2.2 tar 管道同步的缺陷
早年间经常看到有人用tar czf - /data | ssh server "tar xzf -"这样的命令做远程同步。这种方式确实能传输数据,但有几个问题:一是没有增量能力,每次都是全量打包;二是没有断点续传,网络一断整个传输作废;三是没有任何校验机制,传过去的数据对不对完全靠运气。
rsync 天然解决了这三个问题。增量传输、断点续传、校验和比对,这三个特性组合在一起,让 rsync 在绝大多数文件传输场景下都优于 scp 和 tar 管道。
2.3 rsync 适用场景与不适合的场景
适合的场景:
- 服务器数据定期备份到远程存储
- 多台服务器之间同步配置文件或站点目录
- 代码发布时的增量推送
- 迁移服务器时的数据搬移
- 本地目录与远程目录的双向镜像(配合双向同步参数)
不适合的场景:
- 实时同步(rsync 不是实时工具,要配合 inotify 或定时任务才能接近实时)
- 大量小文件的首次全量同步(每次对比文件列表会有额外开销,首次全量传输 10 万个小文件会比 tar 慢)
- 跨平台到 Windows 且无额外软件环境的场景(rsync 在 Windows 上可用,但配置成本比 Linux 高)
3. rsync 的工作机制:增量同步到底是怎么实现的
理解 rsync 的原理,你才能更好地驾驭它。哪怕只会用命令,明白了底层的逻辑,遇到问题排查起来也会从容得多。
3.1 同步核心:文件列表对比
rsync 在开始传输前,会分别获取源端和目标端的文件列表。它并不是简单比较文件名是否相同,而是比较以下几项:
- 文件名,路径
- 文件大小
- 修改时间(mtime)
- 可选的文件指纹(checksum)
只要有一项不同,这个文件就会被列入待传输列表。这就是 rsync 能做到增量同步的基础。默认比较的是大小和修改时间,如果需要更严格的校验,可以加上-c参数强制对比校验值。
3.2 增量传输的细节:断点续传与增量算法
对于单个大文件,rsync 有一种非常聪明的增量算法。它会把源文件切成固定大小的数据块(默认约 700 字节左右),对每个数据块计算弱校验和与强校验和。目标端以同样方式对已有文件做分块校验,两端交换校验信息后,rsync 就能判断出哪些数据块在目标端已经存在,只传输缺失的数据块。
这个机制在日志文件同步时特别有用。比如你有一个 2G 的日志文件,每次新增内容只有几十 M。用 rsync 同步时,它只需要传输新增的那部分数据,而不是整个文件。
单纯依赖修改时间对比可能会漏掉一些文件内容没变但 mtime 被修改的情况,所以-c参数在关键业务场景下要按需开启。开启之后会增加一些计算开销,但换来的是更可靠的一致性。
3.3 传输通道:ssh 还是 rsync daemon
rsync 有两种工作模式:
- 通过 ssh 通道传输,默认端口 22,也是最常见的用法
- 通过 rsync daemon 模式,走独立端口(默认 873)
ssh 模式的好处是无需额外部署服务端,只要目标机器有 sshd 服务,就能直接用。认证走 ssh 的密钥体系,安全可控。daemon 模式下 rsync 自身作为一个服务运行,通常在需要开放到公网的场景或没有 ssh 服务的情况下使用,但这种模式需要注意访问权限配置,只开放必要的模块路径。
我个人的建议是:能用 ssh 模式就别用 daemon,少开一个端口,少一份安全风险。只有在内网中有大量备份任务、想统一管理备份策略时才考虑搭 rsync daemon。
3.4 rsync 的退出码含义
rsync 命令执行后会返回一个退出码。你可能觉得这不重要,但在写脚本、做自动化的时候,退出码就是判断成功与否的唯一标准。
| 退出码 | 含义 |
|---|---|
| 0 | 同步成功 |
| 1 | 语法错误 |
| 2 | 协议不兼容 |
| 3 | 文件选择错误 |
| 4 | 未请求操作 |
| 5 | 请求操作但未执行 |
| 6 | 无法删除源文件 |
| 10 | 套接字 I/O 错误 |
| 11 | 文件 I/O 错误 |
| 12 | rsync 协议数据流错误 |
| 13 | 诊断信息错误 |
| 14 | IPC 代码错误 |
| 20 | 收到 SIGUSR1/20 或 SIGINT 信号 |
| 21 | 等待 worker 时收到错误 |
| 22 | 文件变化导致同步未完成 |
| 23 | 部分文件传输跳过 |
| 30 | 超时等待数据 |
| 35 | 网络连接超时 |
我碰到最多的就是 23 和 11、12。23 通常意味着有部分文件没同步成功,往往和权限或文件被占用有关;11、12 多为网络问题,需要检查带宽和连接稳定性。
4. 实操:从基本命令到自动化备份方案
4.1 基本语法与最小命令
rsync 的语法结构是:
rsync [选项] 源路径 [用户@]目标主机:目标路径最基本的用法:
rsync -av /home/user/data/ backup@192.168.1.100:/backup/data/这条命令的含义是把本机/home/user/data/目录下的所有内容同步到远程主机的/backup/data/目录下。
注意我在这里写的是data/后面带斜杠,而不是data。这个细节非常关键:
- 带斜杠:把目录里面的内容同步过去,等同于
data/下的所有内容 - 不带斜杠:把目录本身同步过去,在目标端会生成
data目录,里面的内容是源目录的完整拷贝
这个区别在同步目录结构时非常重要,很多新手在这里栽过跟头。
4.2 常用参数速查表
rsync 参数多,但真正高频使用的就下面一批。
| 参数 | 作用 | 说明 |
|---|---|---|
| -a | 归档模式 | 等于 -rlptgoD,递归传输并保留权限、时间戳、属主、属组、设备文件等 |
| -v | 显示详细输出 | 同步时能看到每个文件的状态 |
| -z | 传输时压缩 | 适合可压缩数据,如文本、日志、代码,对已压缩格式(jpg、mp4)没意义 |
| -r | 递归传输目录 | -a 已包含 |
| -u | 跳过比目标更新的文件 | 常用于防止覆盖 |
| --delete | 删除目标端多余文件 | 使目标端与源端完全一致,用于镜像场景 |
| --exclude | 排除指定文件或目录 | 支持通配符 |
| --include | 包含指定文件 | 与 --exclude 组合使用实现白名单逻辑 |
| --bwlimit | 限制传输带宽 | 单位 KB/s,避免占满出口带宽 |
| --timeout | 设置 I/O 超时时间 | 超时会自动断开,避免同步卡死 |
| -n | 试运行 | 配合 -v 使用可以预览将执行的操作而不会真正同步 |
| -c | 校验文件内容 | 忽略 mtime,强制检查文件校验值 |
| --partial | 保留部分传输的文件 | 断点续传的关键参数 |
| --progress | 显示传输进度 | 便于观察大文件传输情况 |
| -P | 等于 --partial --progress | 常用组合 |
实际使用中,我的基准命令都是rsync -avz或rsync -avzP。前者用于大多数同步场景,后者用于大文件、长时间传输场景。
4.3 场景一:服务器备份到远程存储
假设你要把一台 Web 服务器的站点目录和数据库备份文件每天同步到另一台备份服务器。
rsync -avzP --delete --bwlimit=5000 /var/www/html/ backup@192.168.1.200:/data/backup/html/这条命令把/var/www/html/下的所有内容增量同步到远程/data/backup/html/,启用压缩传输和断点续传,删除目标端多余文件,并限制带宽不超过 5000KB/s,避免影响正常业务流量。
4.4 场景二:多服务器代码发布
代码发布场景最关心的是增量效率和排除无关文件。
rsync -avz --exclude=".git" --exclude="node_modules" --exclude="runtime/logs" ./ app@10.0.0.5:/var/www/project/排除.git目录避免传仓库元数据,排除node_modules避免传输庞大的依赖包,排除日志目录避免污染线上环境。发布之前先加 -n 预览一下:
rsync -avz --exclude=".git" --exclude="node_modules" --dry-run ./ app@10.0.0.5:/var/www/project/看清单里是不是只有你要同步的文件,确认无误后再去掉 --dry-run 执行。
4.5 场景三:远程拉取到本地
有时候源在远程,目标在本地:
rsync -avzP remote@192.168.1.100:/data/database/ ./backup/database/配合定时任务就能实现每天自动拉取备份。注意同步方向是由源和目标的相对位置决定的,不是固定必须从本地推到远程。
4.6 定时同步:cron 与脚本组合
自动化是 rsync 场景下的基本需求,做法也很简单。先写一个备份脚本,内容大致如下:
#!/bin/bash # 定义变量 SOURCE_DIR="/home/user/project" TARGET_HOST="backup@192.168.1.200" TARGET_DIR="/backup/project" LOG_FILE="/var/log/rsync_project.log" # 执行同步 rsync -avz --delete \ --exclude="*.tmp" \ --exclude="cache/" \ --timeout=300 \ $SOURCE_DIR $TARGET_HOST:$TARGET_DIR >> $LOG_FILE 2>&1 # 检查退出码 if [ $? -eq 0 ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') 同步成功" >> $LOG_FILE else echo "$(date '+%Y-%m-%d %H:%M:%S') 同步失败,退出码: $?" >> $LOG_FILE fi然后添加到 crontab:
0 3 * * * /usr/local/bin/backup_project.sh >> /dev/null 2>&1每天凌晨三点执行一次,悄无声息地完成备份。用 ssh key 免密登录,就不用在脚本里暴露密码了。
提示:首次用 crontab 时,建议把输出重定向到日志文件,别直接丢到
/dev/null,调试阶段看不到报错会很急人。等稳定运行一周再静默也不迟。
5. 增量备份方案:硬链接轮询备份
定时全量同步的思路虽然简单,但有一个隐患:如果源端有文件被误删,--delete会把目标端的备份也删掉。真正的备份方案要能保留多个历史版本,才能在灾难恢复时找回旧数据。
5.1 硬链接轮询备份原理
rsync 结合硬链接技术可以做出一种经典的轮询备份方案。每次备份时生成一个带时间戳的目录,相同内容的文件通过硬链接复用上一次备份的文件存储空间,只有新增或修改过的文件才占用新的磁盘空间。这样既保留了完整的历史版本,又不会导致磁盘用量线性增长。
实现思路是:
#!/bin/bash # 备份目录 BACKUP_ROOT="/backup/project" TODAY=$(date +%Y-%m-%d_%H-%M-%S) PREV=$(ls -1 $BACKUP_ROOT | tail -n 1) # 如果存在上一次备份,以它为硬链接源 if [ -n "$PREV" ]; then rsync -av --delete --link-dest="$BACKUP_ROOT/$PREV" \ /home/user/project/ "$BACKUP_ROOT/$TODAY/" else rsync -av /home/user/project/ "$BACKUP_ROOT/$TODAY/" fi # 清理超过30天的备份 find $BACKUP_ROOT -maxdepth 1 -type d -name "20*" -mtime +30 -exec rm -rf {} \;--link-dest指定了上一次备份目录,rsync 在对比文件时会先检查能否以硬链接方式引用目标端的旧文件。能链接就链接,不能链接才重新传输。这个方案既保留了多版本,又节省磁盘空间。
5.2 恢复备份的步骤
恢复某个历史版本的备份,直接把对应日期的目录拷贝回去:
rsync -avzP /backup/project/2025-01-15_03-00-00/ app@10.0.0.5:/var/www/project/建议目录权限恢复后再测一下服务是否正常,备份恢复了不代表应用一定能跑起来,数据库类文件尤其要注意一致性。
5.3 备份方案中的常见陷阱
第一次用硬链接轮询方案时容易忽略一个问题:--delete会删掉目标端多余文件,但如果目标源目录中有一个文件没有变化,硬链接能保住它的存量存储,不会有任何问题。不过如果你备份的文件是数据库通过 mysqldump 导出的 SQL 文件,每次导出的内容都是新文件,硬链接就发挥不出作用了,需要结合数据库自身的事务日志或 binlog 才能做真正的增量备份。
另外,硬链接轮询方案需要源和备份目录在同一个文件系统。因为硬链接不能跨文件系统创建。如果你的备份盘是独立挂载的,要确认挂载点下这些备份目录在同一个分区内,否则 --link-dest 会失效,退化为全量拷贝。
6. 常见问题与排查实录
任何工具用久了都会遇到各种问题,rsync 也不例外。以下是我在实际运维中遇到频率最高的一批问题和解法。
6.1 同步速度很慢,应该从哪排查
遇到速度慢,先分清是首次全量同步还是增量同步。
首次全量同步的情况下,慢是正常的,特别是数据量大、文件多的时候。但如果你确认是增量同步也慢,重点排查两个方向:
- 网络带宽是否被其他业务占用
- 文件 mtime 是否频繁变化导致大量文件被判定为需要传输
我遇到过最隐蔽的情况:一个服务每时每刻在更新某些文件的 mtime,导致 rsync 每次都会认为文件有变化而重新传输。排查时用rsync -avzc对比一次,发现实际内容并没有变化,于是确认是 mtime 的问题,解法是从应用层调整文件的更新策略,或者在同步时禁用 mtime 对比只用校验值对比。
6.2 退出码 23 的典型原因
退出码 23 表示部分文件没有传输成功。常见原因:
- 目标端目录没有写权限
- 源端文件被进程锁定无法读取
- 文件名包含特殊字符导致无法创建
排查时在命令里加上-v看哪些文件被跳过或报错。大量文件报权限问题,就用-a参数让归档模式保留权限信息,并确认目标用户具备写入权限。
6.3 断点续传的文件会残留吗
rsync 传输过程中断后,目标端会留下一个临时文件。使用--partial参数,中断时的部分文件会保留为后缀.rsync的临时文件。下次同步时 rsync 会基于这些临时文件续传,而不用从零开始。
不想要这种临时文件残留,在脚本里加一条清理命令:
find $TARGET_DIR -name "*.rsync" -delete6.4 --delete 误删数据怎么办
这是 rsync 最危险的参数之一。用了--delete之后,目标端所有源端不存在的文件都会被删除。如果同步方向搞反,数据就彻底没了。
我的建议是:
- 重要数据同步前先备份目标端目录列表
- 用
--dry-run先预览将被删除的文件 - 必要时加上
--exclude保护关键文件
更稳妥的做法是搭配--backup参数,删除前先备份目标侧文件到指定目录:
rsync -av --delete --backup --backup-dir=/backup/deleted/ /source/ /target/这样被删除的文件会先归档到/backup/deleted/,而不是直接消失。
6.5 如何迁移到新服务器
服务迁移时常见操作是把旧服务器数据同步到新服务器:
rsync -avzP --delete --exclude="*.pid" --exclude="/tmp/" root@旧服务器IP:/var/www/ /var/www/注意首次同步时用全量,之后可以连续跑两次。第一次跑完可能还有文件在写,等业务停掉之后再跑一次增量,保证最终一致性。发布前新服务器先验证环境,确认没问题再切换流量。
6.6 大文件同步超时
同步大文件时默认没有超时限制,但一旦网络抖动,进程可能一直挂着。建议设置--timeout=600,如果 10 分钟没有任何数据传输就自动断开,配合--partial实现可恢复的断点续传。这个组合特别适合机房之间网络不稳定的场景。
6.7 中文和特殊文件名处理
rsync 默认情况下不支持某些特殊字符的文件名,尤其是换行符等控制字符。处理方式是加参数:
--iconv=utf-8,utf-8-mac这个参数主要用于 macOS 与 Linux 之间同步时的编码转换问题。普通 Linux 到 Linux 同步一般不需要,但如果两边文件系统编码不一致,加上能省不少事。
7. 进阶:rsync daemon 模式与多目标同步
7.1 daemon 模式配置
虽然大多数场景我用 ssh 模式就够,但如果你在内网要维护大量备份任务,daemon 模式值得了解。
服务端配置文件/etc/rsyncd.conf:
uid = root gid = root use chroot = yes max connections = 10 pid file = /var/run/rsyncd.pid log file = /var/log/rsyncd.log [backup] path = /data/backup/ comment = backup share read only = no list = yes auth users = rsyncuser secrets file = /etc/rsyncd.secrets hosts allow = 192.168.1.0/24/etc/rsyncd.secrets文件写入rsyncuser:密码,权限设置为 600:
chmod 600 /etc/rsyncd.secrets启动服务:
systemctl start rsyncd systemctl enable rsyncd客户端拉取或推送时使用的是模块名而非路径:
rsync -avzP rsyncuser@192.168.1.100::backup/ /local/backup/7.2 多目标同步策略
当你有多个目标服务器需要同步同一份代码时,不能简单地在一条命令里指定多个目标。做法是写一个循环脚本:
#!/bin/bash TARGETS=("10.0.0.5" "10.0.0.6" "10.0.0.7") for host in "${TARGETS[@]}"; do rsync -avz --delete /release/ app@$host:/var/www/project/ >> /var/log/publish.log 2>&1 echo "$(date) $host 同步完成" >> /var/log/publish.log done多目标同步看似简单,但要注意一点:如果某个节点同步失败,脚本需要跳过或重试,不能让失败节点带病上生产。可以在每个节点同步后检查退出码并输出日志,便于后续排查。
7.3 rsync 安全加固建议
rsync 走公网时,几点安全建议:
- 不要用默认端口,修改 daemon 端口或结合防火墙限制来源 IP
- 必须配置密钥认证或强密码策略
- 只开放必要的模块路径,别把整个根目录暴露出去
- 禁用
--delete在公网上的直接使用,误操作风险太高 - 日志定期检查,观察异常同步记录
8. 我的实战记录与经验沉淀
最后分享一下我个人的使用习惯和一些踩坑之后的体会。
8.1 我常用的 rsync 命令模板
日常运维中常用的命令就这几种,我直接列出来供参考。
全量镜像同步(目录结构保持一致、内容完全镜像):
rsync -avzP --delete /源目录/ 目标路径/带排除列表的增量发布:
rsync -avzP --exclude-from=/etc/rsync_exclude.txt /构建产物/ root@目标IP:/应用目录/大文件断点续传:
rsync -avzP --partial --timeout=600 /大文件 /backup/预览效果,不实际执行:
rsync -avzn /源目录/ 目标路径/查看远端目录列表:
rsync --list-only 目标路径/8.2 一个真实场景:跨机房数据迁移
有一次我们需要把近百 GB 的数据库备份数据从 A 机房迁到 B 机房。两个机房之间的专线带宽有限,白天还要走业务流量,只能后半夜跑。
我当时设计了一个分段方案,先做一次不限制带宽的预同步(利用夜间低峰期拉全量),业务切停后再做最后的增量同步。第一次全量因为数据量大,实际跑了将近 5 个小时。但因为有--partial,中间即使断过一次也能续传,没有白费前面的进度。业务停止后的增量同步只传了最近几个小时产生的新文件,十几分钟就完成了。整个过程没有动用物理硬盘,也没有停机太久,这就是 rsync 在迁移场景下的价值。
8.3 自动化备份中的脚本小技巧
有了 cron 还不够,我通常会在备份脚本里加几层保护:
- 备份前检测磁盘空间余量,不足时先告警再决定是否执行
- 生成单独的日志目录,按日期存放日志文件
- 每次备份完成后统计数量变化,用
diff对比上次的文件列表,及时发现异常
举个例子:
# 对比两次备份的文件差异 diff <(ls /backup/$(date -d yesterday +%F)) <(ls /backup/$(date +%F))如果发现文件数量异常减少,立刻排查是不是应用端出问题了,而不是在恢复备份时才发现数据没了。
8.4 rsync 的局限与新工具对比
说到 rsync 的局限,有三个点必须承认:
- 实时性不足,它本质是定时或手动触发的工具
- 首次全量同步速度不如 tar 管道直接,因为需要先扫描文件列表
- 不支持类似数据库 binlog 的变更流式同步
如果真有实时同步需求,可以看 inotify + rsync 组合方案,或者用其他开源实时同步工具。但即便如此,rsync 在增量同步、跨网络传输稳定性、备份版本管理上依然有着不可替代的位置。定期备份、迁移数据、发布代码这些任务,我至今仍主要依赖 rsync。
8.5 最后的经验之谈
rsync 这二十多年能一直保持生命力,核心是因为它解决的是文件同步中最本质的那些问题:效率、准确性、健壮性。参数多,但只要记住-avzP --delete这条主线,再按需叠加排除、限速、超时、预览这些能力,就足以应对绝大多数场景。
我个人体会最深的一件事是:刚开始用 rsync 的时候,总觉得--delete很厉害,处处想用。后来被它删过一次文件,从此养成一个习惯,凡是涉及删除操作的同步,都会先跑一遍--dry-run看看会被删什么。这个习惯救过我很多次,也送给大家。