1. 为什么 Rsync 是运维备份场景里的常青树干了这么多年运维经手的备份工具少说也有十几种从早期的tar加scp组合拳到后来各种商业备份套件兜兜转转一圈下来日常用得最顺手、最频繁的还是rsync。这个工具在 Linux 圈子里属于那种平时不显山露水关键时刻离不开的存在。它的核心价值就一句话增量同步、断点续传、保留属性、跨主机传输四件事一次搞定。很多刚入行的朋友对备份的理解还停留在把文件复制一份到另一个地方用cp或者scp就完事了。小数据量确实没问题但一旦面对几十 GB 甚至 TB 级的目录每次全量拷贝就是灾难——网络带宽吃满、磁盘 IO 飙升、备份窗口越拖越长。rsync解决的就是这个痛点它通过一套叫delta 传输算法的机制只传输源和目标之间有差异的部分哪怕你只改了一个字节它也不会傻乎乎地把整个文件重传一遍。这篇文章我打算把rsync从原理到实操彻底讲透包括它的算法逻辑、常用参数组合、增量备份方案设计、定时任务编排、常见报错排查以及我在实际项目中踩过的那些坑。适合谁看如果你是刚接触 Linux 运维的新手想系统学一个靠谱的备份工具或者你已经在用rsync但总是被各种参数和权限问题搞得头大想搞清楚背后的逻辑那这篇内容应该能帮到你。我会尽量用大白话把原理讲清楚同时给出可以直接抄作业的命令和脚本。2. Rsync 核心机制与算法原理拆解2.1 增量同步到底是怎么实现的rsync最核心的能力就是增量传输但这个增量到底是怎么算出来的很多人其实并不清楚。它的核心算法叫滚动校验rolling checksum思路非常巧妙。打个比方假设你有一本 500 页的书现在要对比两个版本之间的差异。笨办法是一页一页逐字对比效率极低。rsync的做法是先把目标端接收方的文件按固定大小切成一个个数据块比如每块 2KB然后对每个块计算两个校验值——一个是弱校验滚动校验一个是强校验MD5 或 xxhash。这些校验值组成一个校验和列表传回给源端。源端拿到这个列表后对自己这边的文件也做滑动窗口计算。它不需要重新切块而是用一个固定大小的窗口在文件上滑动每滑动一个字节就计算一次弱校验然后拿这个弱校验去匹配目标端传来的列表。一旦弱校验匹配上了再算强校验做二次确认。匹配成功说明这个块在目标端已经存在不需要传输匹配不上的部分就是新增或修改的内容才会被真正传过去。这个机制的好处是即使文件中间插入了一段内容导致后面所有块的边界都错位了滚动校验依然能通过滑动窗口找到匹配的块。这就是为什么rsync在文件内容有插入、删除操作时依然能保持很高的传输效率。注意rsync的增量算法是针对文件内容的不是针对文件名的。如果你把一个大文件重命名了rsync默认会认为这是一个新文件会全量传输。这种情况需要配合--fuzzy参数让它去查找相似文件作为基础。2.2 传输模式与连接方式的选择rsync支持多种传输模式理解这些模式的区别是正确使用它的前提。传输模式命令格式示例适用场景是否加密本地模式rsync -av /src/ /dst/本机不同目录间同步不涉及远程 Shell 模式rsync -av /src/ userhost:/dst/跨主机同步走 SSH 通道是守护进程模式rsync -av /src/ host::module多客户端集中备份到备份服务器可配置本地模式最简单就是同一台机器上不同目录之间的同步常用于做本地快照或者迁移数据。远程 Shell 模式是日常用得最多的底层走 SSH 协议天然加密不需要额外配置服务端。守护进程模式则需要在服务端跑一个rsync --daemon客户端通过host::module的方式连接适合那种多台机器往一台备份服务器上推数据的集中式备份架构。我个人的建议是如果只是几台机器之间的同步直接用 SSH 模式就够了省事又安全。只有当客户端数量多到几十上百台或者需要做精细的访问控制、带宽限制、模块化管理时才值得去搭守护进程模式。2.3 那些容易搞混的参数逻辑rsync的参数多如牛毛但真正高频使用的就那么十几个。我把最容易搞混的几个拎出来单独说。-a和-r的区别-r只是递归复制目录而-a是-rlptgoD的组合等于递归 保留符号链接 保留权限 保留时间戳 保留属组 保留属主 保留设备文件。做备份场景无脑用-a就对了-r基本只在特殊场景下单独使用。结尾斜杠的坑这是新手最容易翻车的地方。rsync -av /src /dst/和rsync -av /src/ /dst/的结果完全不同。前者会在/dst/下创建一个src目录变成/dst/src/...后者则是把/src/里面的内容直接同步到/dst/下。记住一句话源路径结尾加斜杠表示同步这个目录里的内容不加斜杠表示同步这个目录本身。--delete的双刃剑这个参数让目标端和源端保持完全一致源端删了的文件目标端也会跟着删。做镜像备份时非常有用但用错了就是数据丢失的元凶。我见过不止一次有人手滑把源和目标写反加上--delete之后目标端的备份数据被清了个干净。建议配合--dry-run先跑一遍看看会删什么。-z压缩传输在慢速网络上传输文本类数据时-z能显著减少传输量。但如果是已经压缩过的文件比如图片、视频、压缩包再压缩几乎没有效果反而白白消耗 CPU。内网千兆以上的环境通常也不建议开-z因为 CPU 压缩解压的开销可能比省下来的带宽还大。3. 实战场景下的备份方案设计3.1 全量加增量的组合策略单纯用rsync做同步每次都是增量传输这很好。但备份和同步是两回事——同步追求的是两边一样备份追求的是能回到过去的某个时间点。如果你只有一份实时同步的数据万一源端文件被误删或者被勒索软件加密同步过去的目标端也跟着完蛋。所以生产环境里我通常采用全量 增量的组合策略。具体做法是用rsync的--link-dest参数配合硬链接实现类似快照的效果。#!/bin/bash # 备份脚本示例 BACKUP_ROOT/backup/data SOURCE_DIR/data/ TODAY$(date %Y%m%d) LATEST_LINK${BACKUP_ROOT}/latest # 创建今天的备份目录 mkdir -p ${BACKUP_ROOT}/${TODAY} # 执行备份--link-dest 指向上一次的最新备份 rsync -av --delete \ --link-dest${LATEST_LINK} \ ${SOURCE_DIR} \ ${BACKUP_ROOT}/${TODAY}/ # 更新 latest 软链接 rm -f ${LATEST_LINK} ln -s ${BACKUP_ROOT}/${TODAY} ${LATEST_LINK}这个方案的精髓在于--link-dest当某个文件在源端没有变化时rsync不会重新复制一份而是在新备份目录里创建一个指向旧备份文件的硬链接。硬链接不占额外磁盘空间所以虽然你看到每天都有一个完整的目录树但实际占用的空间只有变化部分的总和。恢复的时候每个日期目录都是一个完整的、可直接访问的快照不需要像增量备份那样按顺序回放。提示--link-dest要求源和目标在同一个文件系统上因为硬链接不能跨文件系统。如果你的备份目录和源目录不在同一个分区这个方案就不适用了。3.2 定时任务的编排与注意事项备份脚本写好了接下来就是让它自动跑。cron是最常见的选择但有几个细节不注意就会出问题。# 编辑 crontab crontab -e # 每天凌晨 2 点执行备份 0 2 * * * /bin/bash /opt/scripts/backup.sh /var/log/backup.log 21第一脚本里所有路径必须用绝对路径。cron执行时的环境变量和你在终端里登录时完全不同PATH可能只有/usr/bin:/bin相对路径更是无从谈起。第二记得重定向输出否则cron会把执行结果通过邮件发给你日积月累能把/var/spool/mail撑爆。第三给脚本加锁防止上一次备份还没跑完下一次又启动了。#!/bin/bash # 加锁防止重复执行 LOCK_FILE/var/run/backup.lock if [ -f ${LOCK_FILE} ]; then echo Backup already running, exit. exit 1 fi touch ${LOCK_FILE} trap rm -f ${LOCK_FILE} EXIT # 后续备份逻辑...这个trap ... EXIT的写法很关键它保证无论脚本是正常结束还是中途报错退出锁文件都会被清理掉。我早期写脚本时忘了加这个结果有一次脚本异常退出锁文件残留后面所有备份任务全部被跳过整整一周没有备份数据直到发现磁盘空间没变化才察觉。3.3 带宽控制与传输优化跨机房或者跨地域做同步时rsync默认会吃满带宽把业务流量挤得没法看。这时候--bwlimit就派上用场了。# 限制带宽为 5000 KB/s大约 40 Mbps rsync -avz --bwlimit5000 /data/ userremote:/backup/data/--bwlimit的单位是 KB/s注意是小写的 KB也就是 1024 字节。上面这个例子限制在 5000 KB/s换算成网络带宽大约是 40 Mbps。实际设置时建议留出余量比如你的专线是 100 Mbps业务高峰期占用 60 Mbps那备份限速就设在 30 Mbps 以下比较稳妥。另外传输大量小文件时rsync的性能会明显下降因为每个文件都要经历校验、协商、传输、确认的完整流程。这种情况可以考虑先用tar打包再传或者调整--max-size和--min-size参数分批处理。还有一个容易被忽略的参数是--whole-file简写-W在本地同步或者极高速网络下跳过增量算法直接整文件复制反而更快因为省掉了校验计算的开销。4. 权限、属主与特殊文件的处理4.1 保留权限属性的正确姿势-a参数虽然包含了-p保留权限和-g保留属组和-o保留属主但这里有个前提只有 root 用户才能保留属主信息。普通用户执行rsync -a时-o是无效的目标端所有文件的属主都会变成执行同步的那个用户。如果你确实需要以普通用户身份同步但又要保留属主信息可以考虑用sudo提权或者在目标端配置rsync守护进程时启用fake super模式。不过大多数情况下备份场景直接用 root 跑就行了没必要绕弯子。还有一个细节是ACL 和扩展属性。-a并不包含 ACL 的保留如果你的文件系统上用了 ACL 做精细权限控制需要额外加-A参数保留 ACL加-X参数保留扩展属性比如 SELinux 的上下文。这两个参数在 CentOS 和 RHEL 系的环境里尤其重要漏了可能导致恢复后的文件无法被正确访问。# 完整的备份命令保留 ACL 和扩展属性 rsync -avAX --delete /data/ /backup/data/4.2 符号链接与硬链接的处理rsync对符号链接的处理有好几种模式默认情况下-a中的-l会把符号链接当作符号链接复制也就是在目标端创建一个指向相同路径的链接。但如果链接的目标路径在目标端不存在这个链接就成了断链。如果你希望把符号链接指向的实际文件内容复制过去用-L参数。反过来如果你希望跳过所有符号链接用--no-links。还有一种情况是--copy-unsafe-links它只复制那些指向源目录外部的不安全链接的实际内容源目录内部的链接依然保持为链接。硬链接的保留需要-H参数。默认情况下rsync会把硬链接当作独立文件处理两个指向同一 inode 的文件会被复制成两份独立的数据。加上-H之后目标端也会创建硬链接保持原有的链接关系。这个参数在备份含有大量硬链接的目录比如某些软件的缓存目录时非常有用能大幅节省空间。4.3 排除规则的灵活运用实际备份中我们往往不需要同步所有东西。比如缓存目录、临时文件、日志文件、.git目录这些同步过去既浪费空间又没意义。rsync提供了--exclude和--include两个参数来做过滤。# 排除多个目录和文件类型 rsync -av --delete \ --exclude.git/ \ --excludenode_modules/ \ --exclude*.log \ --excludetmp/ \ --excludecache/ \ /data/ /backup/data/排除规则的匹配顺序很重要。rsync是从左到右依次匹配的一旦某个规则匹配成功后面的规则就不再检查。所以如果你既要排除又要包含某些内容--include必须写在--exclude前面。# 只同步 .conf 文件排除其他所有 rsync -av \ --include*/ \ --include*.conf \ --exclude* \ /etc/ /backup/etc/这个例子里--include*/保证所有目录都会被递归进入--include*.conf保证配置文件被同步最后的--exclude*把其他所有文件都挡掉。顺序不能乱否则要么什么都同步不了要么把不该同步的也带上了。如果排除规则很多可以写到一个文件里用--exclude-from指定。# exclude-list.txt 内容示例 .git/ node_modules/ *.log tmp/ cache/ *.tmprsync -av --exclude-from/opt/scripts/exclude-list.txt /data/ /backup/data/这种方式便于维护改规则不用动脚本本身。5. 常见故障排查与避坑指南5.1 典型报错与解决方案速查用了这么多年rsync遇到的报错五花八门但高频的就那么几个。我整理了一张速查表方便对照排查。报错信息根本原因解决方案rsync: command not found目标端未安装 rsync在目标端安装 rsync 包Permission denied (publickey)SSH 密钥未配置或权限不对检查~/.ssh/authorized_keys权限为 600rsync error: some files vanished传输过程中源文件被删除加--ignore-missing-args或忽略该警告mkstemp failed: Permission denied目标目录无写权限检查目标目录权限和属主connection unexpectedly closed网络中断或超时加--timeout300设置超时重试chown failed: Operation not permitted非 root 用户尝试保留属主用 root 执行或去掉-o参数No space left on device目标磁盘空间不足清理空间或调整备份保留策略其中some files vanished这个警告特别常见尤其是在备份活跃的业务目录时。它的意思是rsync在扫描文件列表时看到了某个文件但真正传输时这个文件已经被删掉了。这通常不是错误只是提示信息不影响其他文件的同步。如果你觉得这个警告太吵可以加--ignore-missing-args让它安静下来。5.2 SSH 连接相关的坑用 SSH 模式做远程同步时有几个坑几乎每个人都会踩一遍。密钥认证不生效明明已经把公钥放到目标端的authorized_keys里了但每次还是要输密码。这种情况九成是权限问题。SSH 对权限的要求非常严格~/.ssh目录必须是 700~/.ssh/authorized_keys必须是 600~目录本身不能有组写权限。任何一条不满足SSH 就会拒绝使用密钥认证。# 修复权限 chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chmod 700 ~连接超时跨地域同步时网络抖动导致 SSH 连接断开是常有的事。rsync本身没有自动重连机制连接一断整个任务就失败了。解决办法是在外层包一层重试逻辑。#!/bin/bash MAX_RETRY3 RETRY0 while [ ${RETRY} -lt ${MAX_RETRY} ]; do rsync -avz --timeout300 /data/ userremote:/backup/data/ break RETRY$((RETRY 1)) echo 第 ${RETRY} 次重试... sleep 10 doneSSH 端口非默认如果目标端的 SSH 不是 22 端口需要用-e参数指定。rsync -avz -e ssh -p 2222 /data/ userremote:/backup/data/5.3 那些年我踩过的真实坑说几个我在实际项目中翻过的车都是血泪教训。第一个坑--delete把备份删没了。有一次做迁移源目录和目标目录写反了加上--delete之后目标端其实是新的源的数据被旧数据覆盖几十 GB 的文件瞬间蒸发。幸好有另一份离线备份才没造成严重后果。从那以后我养成了一个习惯任何带--delete的命令先加--dry-run跑一遍确认删除列表没问题再真正执行。# 先干跑一遍看看会删什么 rsync -av --delete --dry-run /data/ /backup/data/第二个坑中文文件名乱码。跨主机同步时如果两端的 locale 设置不一致中文文件名可能会变成乱码。解决办法是在命令前显式指定LANG和LC_ALL。LANGzh_CN.UTF-8 LC_ALLzh_CN.UTF-8 rsync -av /data/ userremote:/backup/data/第三个坑大文件传输中断后从头再来。rsync默认在传输中断后下次会重新校验并续传但如果目标端的临时文件被清理了就得从头开始。加--partial参数可以让rsync保留未完成的临时文件下次传输时从断点继续。rsync -avz --partial --progress /data/bigfile.iso userremote:/backup/第四个坑inotify 文件数限制。如果你用inotifywait配合rsync做实时同步文件数量多了之后会报Failed to watch ... upper limit的错误。这是因为inotify默认的监控上限太低需要调整内核参数。# 查看当前限制 cat /proc/sys/fs/inotify/max_user_watches # 临时调整 sysctl fs.inotify.max_user_watches524288 # 永久生效写入 /etc/sysctl.conf echo fs.inotify.max_user_watches524288 /etc/sysctl.conf5.4 性能调优的几个实用技巧rsync的性能受很多因素影响调优的核心思路是减少不必要的计算和传输。第一关闭不必要的校验。默认情况下rsync会对传输的文件做校验和验证这保证了数据完整性但也消耗 CPU。在内网可信环境下可以用--no-whole-file的反面——也就是保持增量算法但跳过传输后的校验用--size-only或者--checksum-choicenone较新版本支持来提速。不过这个要谨慎数据完整性比速度重要。第二调整块大小。rsync的--block-size参数控制增量算法中数据块的大小默认是根据文件大小自动计算的。对于超大文件适当增大块大小可以减少校验计算量但也会降低匹配精度。一般不需要手动调整除非你有非常明确的性能瓶颈。第三并行传输。rsync本身是单线程的一次只能同步一个目录。如果你有多个独立的目录需要同步可以写多个rsync进程并行跑但要注意控制并发数别把磁盘 IO 和网络带宽打满。# 并行同步多个目录 rsync -av /data/dir1/ /backup/dir1/ rsync -av /data/dir2/ /backup/dir2/ rsync -av /data/dir3/ /backup/dir3/ wait echo 所有同步任务完成第四用更快的校验算法。较新版本的rsync支持xxhash算法比默认的 MD5 快很多。可以用--checksum-choicexxh128启用。不过这个需要两端版本都支持老版本可能不认这个参数。6. 从单机同步到集中备份架构的演进6.1 守护进程模式的配置要点当客户端数量多起来之后SSH 模式的管理成本就上来了——每台客户端都要配密钥、开防火墙、管理账号。这时候可以考虑在备份服务器上跑rsync守护进程客户端通过模块名访问。服务端的配置文件默认在/etc/rsyncd.conf一个典型的配置长这样# 全局配置 uid root gid root use chroot yes max connections 20 pid file /var/run/rsyncd.pid log file /var/log/rsyncd.log transfer logging yes # 模块配置 [backup_data] path /backup/data comment 数据备份目录 read only no write only no hosts allow 192.168.1.0/24 hosts deny * auth users backupuser secrets file /etc/rsyncd.secretssecrets file里存的是用户名和密码格式是用户名:密码权限必须设为 600。客户端连接时用--password-file指定密码文件实现免交互。# 客户端命令 rsync -av --password-file/etc/rsync.pass /data/ backupuser192.168.1.100::backup_data/守护进程模式的好处是集中管理权限、可以限制来源 IP、支持模块化隔离、传输不加密但速度快。缺点是密码是明文存储在文件里的安全性不如 SSH 密钥。所以一般建议只在可信内网使用跨公网还是走 SSH 模式。6.2 备份策略的制定与保留周期工具会用之后更重要的是策略。备份不是越多越好磁盘空间是有限的保留太多历史版本既浪费空间又增加管理复杂度。我通常按GFSGrandfather-Father-Son策略来设计保留周期每天保留最近 7 天的日备份每周保留最近 4 周的周备份每月保留最近 12 个月的月备份。配合前面讲的--link-dest硬链接方案实际占用的空间远小于7412个完整副本的总和。清理旧备份的脚本可以这样写#!/bin/bash BACKUP_ROOT/backup/data # 删除 30 天前的备份 find ${BACKUP_ROOT} -maxdepth 1 -type d -mtime 30 -exec rm -rf {} \;注意find的-mtime 30是按修改时间算的目录的修改时间在创建新文件时会更新所以这个判断基本准确。但如果你用硬链接方案删除旧目录时要注意不要误删了还被其他备份引用的硬链接文件——rm -rf删除目录项时硬链接的引用计数会减一只有计数归零时数据才真正被删除所以是安全的。6.3 监控与告警的接入备份做了不等于备份有效。我见过太多案例备份脚本跑了半年结果真要恢复时发现数据是空的或者不完整。所以备份监控是必须的。最基本的监控是检查备份任务的退出码。rsync的退出码含义如下退出码含义0成功1语法错误2协议不兼容23部分文件传输失败24源文件在传输中消失30超时35连接超时退出码 23 和 24 比较特殊它们表示部分成功通常是因为权限问题或者文件被删除。如果备份脚本里直接判断$? -ne 0就告警这两个码会频繁触发误报。建议把 23 和 24 视为警告而非错误记录日志但不告警。更进一步的监控是校验备份数据的完整性。可以定期从备份中随机抽取几个文件计算校验和与源端对比。或者更简单粗暴一点定期做一次恢复演练把备份数据恢复到测试环境验证可用性。# 备份完成后发送通知示例用邮件 if [ $? -eq 0 ]; then echo 备份成功: $(date) | mail -s Backup OK adminexample.com else echo 备份失败退出码: $? | mail -s Backup FAILED adminexample.com fi监控这块我不展开讲太多核心思想就是备份任务的成功执行只是第一步备份数据的可恢复性才是最终目标。定期验证、定期演练比任何监控指标都重要。7. 我在实际项目中的几点体会rsync这个工具入门容易精通难。参数就那么多但组合起来能应对的场景千变万化。我最大的体会是不要迷信默认配置也不要盲目堆参数。每加一个参数都要清楚它解决什么问题、带来什么副作用。比如-z压缩在内网环境基本是负优化--delete很强大但必须配合--dry-run使用-a看似万能但在涉及 ACL 和 SELinux 的环境里还需要-AX补刀。这些细节文档里不会特意强调只有真正踩过坑才会记住。另一个体会是备份方案要简单可靠不要追求花哨。我见过有人用复杂的脚本加多层判断做备份结果脚本本身出了 bug备份静默失败了好几个月。反而是那种最朴素的rsync加cron加日志监控的组合稳定跑了几年没出过问题。工具越简单出错的环节越少排查起来也越容易。最后分享一个小技巧如果你不确定一条rsync命令会做什么永远先加--dry-run和-v跑一遍。它会告诉你哪些文件会被传输、哪些会被删除但不会真正动任何数据。这个习惯帮我避免了至少三次重大事故。花三十秒干跑一遍比事后花三天恢复数据划算得多。
阅读完成 · 觉得有帮助?