ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Rsync增量同步详解:从算法原理到生产环境备份实践

Rsync增量同步详解:从算法原理到生产环境备份实践 先说一个几乎所有用Rsync的人都会遇到的场景你辛辛苦苦把几十GB的目录从服务器A拷到服务器B过两天那边改了一批文件又要整包重传scp跑了一整夜还没完。用Rsync就不会有这种体验它先对比两边差异再只传变化的部分第二次同步往往只花几分钟。这也是为什么到现在云盘、网盘、备份工具、发布系统里都在用它做底层同步。这篇文章没有按官方手册来写就是我基于实际项目里反复使用、踩坑、优化之后的一份完整整理。内容覆盖Rsync的增量算法原理、本地与远程同步的常用姿势、daemon服务端配置、基于快照的备份方案以及同步慢、丢文件、连接失败这几类高频故障的排查思路。不管是刚接触命令行的新手还是已经在生产环境里跑同步脚本的人应该都能从中找到有用的东西。1. Rsync增量算法的底气不只是“跳过相同的文件”很多人把Rsync理解成“传文件工具”这没错但不够准确。Rsync真正的核心价值是“增量传输”它这个增量可不是简单对比一下文件名和大小那么简单。1.1 它到底怎么做到“只传差异”当年我第一次用Rsync以为它和cp相比只是“支持断点续传”。后来在一次跨机房的超大文件同步中我观察流量曲线才意识到如果文件没变化它几乎不产生网络流量如果文件中间一段改了它也不会整个重传而是只传变化的那一小段。这里面的关键机制是滚动校验和rolling checksum加强弱校验码组合。简单说Rsync会把文件切成固定大小的块比如默认每块约700字节实际是按数据长度动态调整的然后给每个块计算一个“弱校验和”和一个128位的“强校验和”。接收端把自己已有文件的所有块校验值发给发送端发送端拿着本地文件去逐个比对。如果某个块的校验值能匹配上说明这块内容两边一致不用传匹配不上的就把这块内容连同它在文件里的位置信息一起传过去。可以打个比方你和同事各有一本几百页的书同事在部分页面上做了修改但他不知道具体改了哪些地方。Rsync的做法是让同事先把每一段的页码和指纹发给你你对着自己的书一页一页比对发现哪几段对不上就只把那几段寄过去。这个效率远比“把整本书重新寄一遍”高得多。在远程同步场景下Rsync会分两步走第一步先对比文件清单通过文件大小和修改时间快速筛掉没有变化的文件第二步才对真正需要更新的文件进入块级比对流程。这个设计让它在“大量文件、少量变化”的场景下表现得极其出色。1.2 同盘同步时为什么也很快有朋友问过我一个很有意思的问题同一台机器上把目录A同步到目录BRsync为什么也比cp快答案在于Rsync在本地模式下的处理方式。本地同步时Rsync不需要走网络那套校验逻辑它直接用文件大小和修改时间判断是否需要复制。如果两边的文件大小一致且mtime一致它就跳过该文件。cp做不到这种判断它是无脑覆盖tar打包再解包更是全量处理。所以即便在同一块硬盘上Rsync的“跳过已相同文件”的特性也能帮你省下大量IO时间尤其是在同步包含几十万文件的目录时体感差别非常明显。1.3 为什么比scp、cp、tar都更适合长期同步这里整理一个直观对比工具默认行为能否增量能否保留权限/链接适合场景scp全量覆盖不能基本不保留一次性传文件cp全量覆盖不能有限保留同机临时复制tar ssh打包传整个内容不能可以保留整目录迁移rsync增量传输能能且可选开关很多备份、定期同步、大规模分发Rsync还有一个隐藏优势它可以在传输过程中通过SSH通道加密也可以使用daemon模式走自己的873端口可以限速、断点续传、备份被覆盖的文件、排除指定目录。这些能力组合起来基本覆盖了我在服务器数据同步上能想到的所有需求。2. 三分钟跑通本地同步与远程推送的常用姿势我知道很多人看文档最烦的就是参数一大堆。所以这一节直接用命令说话每条命令我都标了“这行是干嘛的”。2.1 本地目录同步从最保守到最常用先看最简单的本地同步rsync -av source/ /backup/source/这里的-a是归档模式等于同时开了-rlptgoD也就是递归、保留符号链接、保留权限、保留mtime、保留属主和组、以设备文件方式处理特殊文件。-v是显示过程。尾部source/后面那个斜杠很关键带斜杠表示同步目录里面的内容到目标路径不带斜杠表示把source这个目录本身也带过去。比如rsync -av source /backup/这条会把/backup/source整个目录创建出来而前面那条则是把source里的内容直接放进/backup/source/下。如果你希望两边完全一致也就是源端删除的文件目标端也删除就要加--deletersync -av --delete source/ /backup/source/但我必须提醒--delete是把双刃剑。在第一次使用这个参数之前先跑一次带--dry-run的模拟同步确认会删除哪些文件再决定要不要真删rsync -av --delete --dry-run source/ /backup/source/2.2 远程推送和拉取走SSH的做法远程同步最推荐的方式是走SSH通道因为你的认证体系、加密体系都可以直接复用不用额外维护一个常驻服务。推送命令长这样rsync -avz -e ssh -p 22 /data/app/ user192.168.1.100:/data/app/-z是传输时压缩适合文本类文件比较多、带宽有限的情况。-e用于指定远程shell默认是ssh如果端口不是22或者你想指定私钥就在这儿写rsync -avz -e ssh -i /home/user/.ssh/id_ed25519 -p 2222 \ /data/app/ user192.168.1.100:/data/app/想从远程拉取到本地把源和目标的顺序换一下就行rsync -avz -e ssh -p 22 user192.168.1.100:/data/app/ /data/app/这里有一个非常实用的小经验远程同步时如果网络不稳定加--partial可以保留部分传输的文件下次再跑会自动续传。再加--progress就能看到实时进度和速度。这两个参数我基本每次远程同步都会用连写就是-Prsync -avzP -e ssh -p 22 /data/app/ user192.168.1.100:/data/app/2.3 必会参数速查表用多了会发现日常操作其实只用得到那么十来个参数。下面是我整理的速查表按使用频率排序参数作用提醒-a归档模式保留权限、链接、时间等日常同步首选-v显示详细信息排错时配合-h更好读-z传输时压缩带宽小、文本多时收益大-P显示进度并保留部分传输相当于--partial --progress--delete删除目标端多余文件先--dry-run再实际执行--dry-run模拟演练不实际改动高风险操作前必须要跑--exclude排除指定路径或模式支持通配符--exclude-from从文件读取排除规则备份脚本里常用--bwlimit限制传输带宽单位KB/s避免占满生产带宽--timeout空闲超时断开网络不稳定时建议设置--itemize-changes逐文件显示变化类型看“会改什么”很清晰--stats传输结束后输出统计用于观察效率组合起来的一个推荐写法rsync -avzP --delete --bwlimit5000 --timeout60 \ -e ssh -p 2222 /data/app/ user192.168.1.100:/data/app/这条命令兼顾了增量、压缩、进度、容错、限速适合在生产环境定时任务中使用。3. daemon模式把Rsync变成真正的服务如果只是偶尔手动同步SSH模式的rsync已经够用了。但如果你要维护一台“备份接收机”或者要给机房内多台机器提供一个文件分发源那每次都用SSH登录再同步就有点笨重。这时可以考虑把Rsync跑成daemon服务通过rsync://协议对外提供模块化同步。3.1 什么时候应该选择daemon模式我个人的判断标准是三条满足任意一条就值得部署daemon你希望客户端只具备同步能力而不需要登录Shell或使用系统账号。你需要集中管理多个同步目录并且对每个目录设置独立的读写权限、允许IP和最大连接数。你想用Rsync自己的认证机制auth users加secrets file来控制同步访问而不想分发SSH密钥。daemon模式还有一个好处它默认只监听在873端口配合防火墙和iptables规则暴露面比SSH小而且它的配置文件和系统账号隔离得很干净权限管理非常清晰。3.2 从零配置一个可用的rsyncd我的做法是在/etc/rsyncd.conf里写配置下面这个例子可以直接套用uid rsync gid rsync use chroot yes max connections 10 pid file /var/run/rsyncd.pid log file /var/log/rsync.log timeout 300 read only yes [backup] path /data/backup/ comment Backup server for production nodes auth users syncuser secrets file /etc/rsyncd.secrets hosts allow 192.168.1.0/24 hosts deny * list false这里提几处关键设计uid和gid我设置成专用账号rsync而不是直接用root。虽说daemon方式也可以在配置文件里单独为模块指定root权限但安全审计的时候很麻烦。用一个最小权限账号更稳妥。use chroot yes表示客户端进入模块后会被锁在path目录内这是防止路径穿越的关键选项。如果开了chroot模块path指向的目录必须完整存在于文件系统中且其中不能有跨分区的符号链接指向外部否则同步会报错。hosts allow和hosts deny是访问控制列表比在防火墙层面做限制更灵活。生产环境我一般会写具体网段不写*这种全开规则。list false是隐藏模块列表。客户端不带模块名直接敲rsync 服务器地址::时不会看到这个模块避免泄露目录信息。rsyncd.secrets文件的格式是一行一个账号syncuser:StrongPassw0rd注意这个文件的权限必须是600并且属主要和rsync运行账号匹配chown rsync:rsync /etc/rsyncd.secrets chmod 600 /etc/rsyncd.secrets配置完成后启动服务systemctl enable rsyncd systemctl start rsyncd如果是最小化安装的系统rsync服务可能没装先用包管理器装一下# Debian/Ubuntu apt install rsync # RHEL/CentOS yum install rsync3.3 客户端怎么访问daemon模块访问daemon模块的语法和SSH模式不太一样。以前面的backup模块为例rsync -avzP --delete rsync://syncuser192.168.1.100:873/backup/ /data/backup/也可以用双冒号简写方式rsync -avzP --delete syncuser192.168.1.100::backup/ /data/backup/如果你只想列出一个daemon服务器上开放了哪些模块不带路径访问就行rsync rsync://192.168.1.100:873/输入账号密码后就能看到模块列表。这里有个坑如果list false这个命令的表现会比较奇怪可能只返回空列表或直接提示没有模块。这不是服务挂了是配置文件里有意隐藏了。3.4 daemon部署容易踩的三个坑第一个坑是防火墙。很多时候本机连873端口都通但客户端就是连不上十有八九是防火墙没放行。记得开放端口firewall-cmd --permanent --add-port873/tcp firewall-cmd --reload如果是云服务器还要在安全组里放行873/tcp。第二个坑是SELinux。RHEL/CentOS默认开启SELinux时rsync daemon的写权限可能会被拦。最简单的处理是把SELinux布尔值打开setsebool -P rsync_client 1 setsebool -P rsync_full_access 1不想开放full access的话也可以用audit2allow按审计日志生成定制策略这个就看你对SELinux的熟悉程度了。第三个坑是chroot下的符号链接。模块目录里如果存在指向外部的符号链接开了use chroot yes后同步就会报错因为客户端被锁在chroot环境里无法访问外部路径。排查时看到“symlink has no referent”这类信息基本就是这个原因。要么去掉外部链接要么关掉chroot并配合防火墙做访问控制二选一。4. 真正的备份方案快照、过滤与清理策略日常手动同步和“一套能落地的备份方案”是两码事。这里说的备份方案是指跑在定时任务里、不需要人工干预、并且能够在数据出问题时回滚到任意时间点的完整流程。基于Rsync这个方案的成本可以做到很低。4.1 备份目录规划按时间戳维护多版本我最常用的目录结构是这样的/backup/production/ ├── latest - 20250120_030001/ ├── 20250116_030001/ ├── 20250117_030001/ ├── 20250118_030001/ ├── 20250119_030001/ └── 20250120_030001/latest是一个软链接永远指向最近一次成功备份。这样脚本里要恢复到“最近状态”时直接走/backup/production/latest/即可不需要关心具体时间戳。每次同步命令写入一个带时间戳的新目录BACKUP_DIR/backup/production/$(date %Y%m%d_%H%M%S) rsync -a --delete /data/production/ $BACKUP_DIR/ ln -snf $BACKUP_DIR /backup/production/latest这样做的好处是任何一次备份完成后latest都会切换到新目录备份过程中如果中断不影响已有的历史版本。4.2 用--link-dest做低成本快照上面这个方案有个问题如果每天备份几十个版本会占掉大量磁盘空间。因为每次都是全量复制。Rsync的解决办法是--link-dest参数。它的原理是如果当前要备份的文件和--link-dest指定目录里的文件完全相同就不复制内容而是创建硬链接指向那个旧文件。rsync -a --delete \ --link-dest/backup/production/latest \ /data/production/ $BACKUP_DIR/由于硬链接不额外占用磁盘空间第一次全量备份占多少空间之后每天的新版本通常只占“实际变化文件”的大小。对备份源里大量文件长期不变的情况来说效果非常明显。我负责的一个项目原始数据约200GB跑了90天快照后整个/backup/production目录实际占用只增加了不到15GB。需要注意--link-dest要求目标目录和--link-dest目录在同一个文件系统内跨分区或跨网络磁盘无法创建硬链接。4.3 过滤规则与限速备份过程中最怕把日志、缓存、临时文件一起同步过去。用--exclude-from把排除规则写到外部文件里比在命令行里写一堆--exclude更清晰。下面是一个常见的exclude.txt*.log *.tmp .cache/ node_modules/ .git/ __pycache__/配合命令rsync -a --delete \ --exclude-from/etc/rsync_exclude.txt \ --link-dest/backup/production/latest \ /data/production/ $BACKUP_DIR/如果这台机器还要对外提供服务务必加上限速参数否则同步高峰期会抢光业务带宽。--bwlimit5000表示限制为5000KB/s也就是约5MB/srsync -a --delete \ --exclude-from/etc/rsync_exclude.txt \ --bwlimit5000 \ --link-dest/backup/production/latest \ /data/production/ $BACKUP_DIR/4.4 定时清理与完整脚本我习惯把整个流程写成一个脚本放进crontab。加一段保留策略例如只保留最近14天#!/bin/bash BACKUP_ROOT/backup/production BACKUP_DIR$BACKUP_ROOT/$(date %Y%m%d_%H%M%S) mkdir -p $BACKUP_DIR rsync -a --delete \ --exclude-from/etc/rsync_exclude.txt \ --bwlimit5000 \ --timeout300 \ --link-dest$BACKUP_ROOT/latest \ /data/production/ $BACKUP_DIR/ if [ $? -eq 0 ]; then ln -snf $BACKUP_DIR $BACKUP_ROOT/latest # 清理14天之前的备份目录 find $BACKUP_ROOT -maxdepth 1 -type d -name 20* -mtime 14 -exec rm -rf {} \; else echo $(date) backup failed /var/log/rsync_backup.err rm -rf $BACKUP_DIR fi这里我额外提两个细节mkdir -p要在rsync之前先建目录否则--link-dest指向latest时如果目标目录还没创建rsync会直接在根下新建一个latest的同名目录导致软链接被覆盖。find清理目录时用-mtime 14而不是硬编码天数这样即使某次备份失败导致个别目录缺失也不会误删其他时间点。crontab里这样写0 3 * * * /opt/scripts/backup_production.sh /var/log/rsync_backup.log 21凌晨3点执行系统负载低对生产影响小。5. 同步慢、丢文件、连接失败从这几类故障说起Rsync本身很稳定但实际跑起来还是会遇到各种状况。我按自己踩坑的频率把故障分成了三类每一类都给出定位和解决思路。5.1 连接与授权类故障最典型的错误是rsync: connection unexpectedly closed。我看到这个报错时排查顺序是固定的先确认端口能不能通telnet 192.168.1.100 873或nc -vz 192.168.1.100 873。再确认防火墙和安全组是否放行。然后看daemon服务状态systemctl status rsyncd。最后看日志tail -50 /var/log/rsync.log。如果在客户端使用daemon方式时提示ERROR: auth failed on module backup先别急着怀疑密码错。检查顺序应该是/etc/rsyncd.secrets文件权限是否为600、属主是否正确、密码文件里有没有多余的空格或空行。我遇到过最隐蔽的一次是密码文件在Windows上编辑后带上了CRLF换行Linux端解析时把\r也算进了密码导致怎么都对不上。如果提示的是ERROR: Unknown module backup除了模块名拼写错误以外还有一种可能是模块配置里的path目录不存在或权限不足daemon会直接拒绝暴露这个模块。5.2 数据不一致与误删类故障--delete导致数据丢失是我最想强调的坑。那种“备份脚本跑完源端数据出现了大面积消失”的事故几乎都源于对--delete的误用。举一个具体场景你想把/data/app同步到备份服务器命令写成rsync -av --delete /data/app/ rootbackup:/backup/app/如果备份服务器的目标路径写错了或者模块映射的path不对rsync会觉得“目标端目录里这些文件在源端不存在”于是全部删掉。我在一次演练中就亲眼见过因为daemon模块path指向了外层目录一条同步命令把备份机上另外几个重要目录清掉了。所以我的习惯是所有带--delete的同步第一次执行前必须加--dry-run。关键备份路径脚本里加--backup --backup-dir/path/to/backup_archive让被覆盖或删除的文件先挪到另一个目录里而不是直接消失。rsync -av --delete \ --backup --backup-dir/backup/archive_$(date %Y%m%d) \ /data/production/ $BACKUP_DIR/另一个与数据相关的问题是“为什么每次同步都全量跑”。最常见的原因是mtime不一致比如两边时区设置不同或文件在源端被touch过但内容没变。解决办法是在同步时加--modify-window5允许mtime有5秒的误差如果源端和备份端只有新增没有双向修改也可以直接使用--size-only只看大小不看时间。5.3 性能类故障小文件太多怎么办Rsync在“大量小文件”的目录上性能会很难看。比如同步一个包含几十万个缓存文件的目录即使每个文件只有几KB光消耗在目录遍历、单文件元数据对比上的时间就非常长。我的应对方案是分层处理对于必须实时同步的小文件目录调整同步频率比如从每分钟一次降到每5分钟一次同时配合--timeout防止卡死。对于可以延迟同步的批量小文件先在源端打包成一个大压缩包再同步最后在目标端解包。这样能显著减少文件数量带来的IO损耗。在daemon模式下如果传输的是大量文本类小文件保持-z压缩收益很大但如果文件已经是图片、视频、压缩包-z反而会白白消耗CPU建议去掉。定位性能瓶颈还有一个实用命令跑一次不带--delete的同步在结束时看--stats输出。留意Number of files transferred和Total transferred file size这两个数字。如果传输文件很少但耗时很长瓶颈在目录遍历和元数据IO如果传输量大瓶颈在带宽或磁盘写入。5.4 一个通用的验证思路不管你用Rsync做什么我建议同步完成后都主动做一次校验而不是等到用的时候才发现数据有问题。最简单的校验方式是rsync -av --dry-run --itemize-changes /data/production/ /backup/production/latest/这条命令不会改动任何文件只会逐项列出“如果现在执行同步会变更哪些文件”。正常情况下一次成功备份后立刻运行它输出应该是空的。只要输出里有内容就说明两边没有完全一致。如果需要在传输完成时就知道结果可以在脚本里加上--checksum强制校验内容但要注意这会增加大量IO开销尤其是在大文件场景下差不多等于把文件全部读一遍。我的建议是日常备份不要开--checksum每月做一次完整校验或者在有疑点时再开。最后分享几个我的使用习惯rsync用了这么多年真正让事故率降下来的就是下面这几个看似不起眼的习惯第一凡是带--delete的命令必须配--dry-run演练一次。第二脚本里同步完成之后一定会检查退出码$?不是0就发告警而不是闷头跑完下一个定时任务。第三关键目录我都会加一层--backup兜底哪怕多占点磁盘也好过误删后从磁带里恢复的噩梦。还有一个很多人不知道的小技巧如果有多组目录需要从同一台机器同步到同一目标把目录清单写进--files-from文件一次rsync进程就能处理完不用起多个并发任务也避免大量小文件场景下重复遍历目录带来的开销。rsync -av --files-from/etc/sync_list.txt /data/ /backup/Rsync这个工具上手门槛很低但真正用得顺手、用得不提心吊胆靠的还是对增量原理和边界条件的理解。希望这篇整理能让你少走点弯路。
返回列表