【Linux从入门到精通】第38篇:定时数据同步神器——rsync与inotify
目录
一、引言:rsync凭什么成为同步标配?
二、rsync的增量同步原理
2.1 rsync如何判断“哪些部分变了”
2.2 由原理决定的适用场景
三、rsync核心参数与实战
3.1 常用参数详解
3.2 源路径末尾斜杠的重要区别
3.3 用-n(dry-run)先测试
3.4 实战:网站文件同步脚本
四、配合crontab定时执行
五、inotifywait:从定时到实时
5.1 为什么还需要inotify?
5.2 安装inotify-tools
5.3 基本使用
5.4 实时同步脚本
5.5 生产化改造:syncthing模式
六、rsync vs scp vs NFS
七、本篇小结
动手练习
八、下篇预告
一、引言:rsync凭什么成为同步标配?
在日常运维中,有一个操作几乎每天都会遇到:把A服务器上的文件同步到B服务器。
可以用scp:
bash
scp -r /var/www/html/ backup-server:/backup/
scp的问题很明显:不管文件有没有变化,每次都全量拷贝。对于几百MB的网站目录,这效率太低了。
rsync的独特优势在于增量同步——它只传输变化了的部分。一个100MB的日志文件,如果今天只追加了10行,rsync只传这几KB的数据,而不是重新拷贝整个文件。
这个特性让rsync成为数据同步的事实标准,几乎所有备份工具(Time Machine、Duplicity、rsnapshot)底层都调用了它。
二、rsync的增量同步原理
2.1 rsync如何判断“哪些部分变了”
rsync的核心算法分为三步:
第一步:生成校验和列表
rsync把目标端已存在的文件按固定大小的块切割,对每一块计算两个校验和:一个快速的弱校验(rolling checksum,滚动哈希)和一个精确的强校验(如MD5或SHA256)。
第二步:匹配
对于源文件,rsync逐字节滑动,算出每个位置的弱校验和。当发现某个位置的弱校验和与目标端某块匹配时,再用强校验确认。如果两个校验和都匹配,说明这个块在目标端已经存在,不需要传输。
第三步:只传不匹配的数据
不匹配的部分,源端直接发送给目标端。目标端用接收到的数据块加上已有的匹配块,拼接出完整的文件。
一个直观的类比:
假设你修改了某段文字中的一句话,对方手里有旧版本。你不用把整段重新打一遍,只需要说:“把第3句话改成'xxxxx',其他保持不变。”——对方的操作就是根据旧稿和你的修改指令,组合出新稿。
这就是rsync的智慧:用少量计算换取大量传输的节省。
2.2 由原理决定的适用场景
理解了这个机制,就能判断rsync擅长什么、不擅长什么:
非常适合:文件很大但改动很小(日志追加、数据库增量备份)、大量文件中有少数变化(网站代码同步)
不太适合:大量全新文件(校验和匹配率低,和scp差不多)、极小的文件(校验和计算的开销相对传输而言不可忽略)
三、rsync核心参数与实战
3.1 常用参数详解
bash
rsync -avz /source/path/ user@backup-server:/dest/path/
核心三参数 -avz:
| 参数 | 全称 | 作用 |
|---|---|---|
-a | archive(归档模式) | 这是一个组合参数,等于-rlptgoD。保留符号链接、权限、时间戳、属主、属组等所有元信息,并递归同步子目录 |
-v | verbose | 显示详细的同步过程(哪些文件被传输了) |
-z | compress | 传输时压缩数据。局域网内rsync不建议加-z(压缩/解压反而耗CPU),跨网络传输时开启有明显加速效果 |
其他常用参数:
| 参数 | 作用 | 使用场景 |
|---|---|---|
-P | 等于--partial --progress:断点续传 + 显示进度条 | 大文件传输必备 |
--delete | 目标端删除源端没有的文件 | 镜像同步(目标端会变成源端的精确副本) |
--exclude | 排除特定文件/目录 | --exclude='*.log'不同步日志 |
--bwlimit | 限制传输带宽(KB/s) | --bwlimit=1000限制1MB/s,避免吃满带宽 |
-n | dry-run,模拟执行不实际传输 | 测试命令无误后再正式执行 |
-u | update,只同步更新的文件 | 避免覆盖目标端更新的文件 |
3.2 源路径末尾斜杠的重要区别
这是rsync最容易踩的坑:
bash
# 有斜杠:复制目录的"内容"到目标 rsync -av /source/ /dest/ # 结果:/dest/ 下直接是 file1, file2... # 无斜杠:复制目录"本身"到目标 rsync -av /source /dest/ # 结果:/dest/source/ 下是 file1, file2...
记忆技巧:有斜杠 = "打开这个目录,把里面的东西倒出来";无斜杠 = "把这个目录连包装一起搬过去"。
3.3 用-n(dry-run)先测试
在生产环境中,永远先模拟运行:
bash
# 先模拟,确认要同步的文件列表符合预期 rsync -avz --dry-run /source/ backup:/dest/ # 确认无误后去掉 -n 正式执行 rsync -avz /source/ backup:/dest/
3.4 实战:网站文件同步脚本
bash
#!/bin/bash # web_sync.sh - 将网站文件同步到备份服务器 SOURCE="/var/www/html/" DEST="backup-server:/backup/www/" LOG="/var/log/rsync_www.log" echo "=== $(date '+%Y-%m-%d %H:%M:%S') 开始同步 ===" >> "$LOG" rsync -avz \ --delete \ --exclude='*.log' \ --exclude='.git/' \ --exclude='node_modules/' \ "$SOURCE" "$DEST" >> "$LOG" 2>&1 if [ $? -eq 0 ]; then echo "同步成功" >> "$LOG" else echo "同步失败!" >> "$LOG" fi
参数说明:
--delete:备份服务器上删除源端已移除的文件,确保备份是源端的精确镜像--exclude:不同步日志文件和依赖包目录,减负增效
四、配合crontab定时执行
将rsync与第16篇的crontab结合,实现定时备份:
bash
# 编辑crontab crontab -e
text
# 每天凌晨3点同步网站文件 0 3 * * * /opt/scripts/web_sync.sh # 每小时同步一次日志归档 0 * * * * rsync -avz /var/log/app/ backup-server:/archive/logs/ --exclude='*.gz'
五、inotifywait:从定时到实时
5.1 为什么还需要inotify?
crontab的最小粒度是1分钟,而且它不管文件变没变,到点就执行。对于某些场景——比如两台Web服务器的代码同步,开发更新了代码,你希望立刻同步到生产服务器——分钟级延迟太慢了。
inotify是Linux内核提供的事件驱动机制,能实时监控文件系统的变化(创建、修改、删除、移动)。inotifywait是它的命令行工具。
5.2 安装inotify-tools
bash
sudo apt install inotify-tools -y # Ubuntu/Debian sudo dnf install inotify-tools -y # CentOS/RHEL
5.3 基本使用
bash
# 监控一个目录的所有文件变化 inotifywait -m /path/to/watch
选项说明:
-m:持续监控(不加-m的话,检测到第一个事件后就退出)-r:递归监控子目录-e:指定监控哪些事件-q:安静模式,不输出多余的启动信息
常用事件:
| 事件 | 含义 |
|---|---|
modify | 文件内容被修改 |
create | 文件或目录被创建 |
delete | 文件或目录被删除 |
move | 文件或目录被移动/重命名 |
attrib | 权限、时间戳等属性变化 |
5.4 实时同步脚本
bash
#!/bin/bash # realtime_sync.sh - 监控目录变化并实时同步 SOURCE="/var/www/html/" DEST="backup-server:/var/www/html/" # 持续监控文件变化 inotifywait -m -r -e modify,create,delete,move --format '%w%f' "$SOURCE" | while read FILE; do echo "[$(date)] 检测到变化: $FILE" rsync -avz "$SOURCE" "$DEST" done
工作流程:
inotifywait -m持续监控源目录一旦检测到文件变化(修改、创建、删除、移动),立刻输出变化的文件路径
while read FILE循环读取每一行输出触发rsync将整个目录增量同步到目标服务器
性能提醒:如果文件变化极频繁(如每秒几百次写入),inotify会频繁触发rsync,可能导致rsync实例堆积。在日志目录等高频写入场景下,建议用
--exclude过滤,或者在循环内部加一个简单的间隔控制(如两次同步至少间隔3秒)。
5.5 生产化改造:syncthing模式
上述简洁版脚本适合轻量使用。对于生产环境,可以加入以下改进:
批处理而非逐文件触发:不在一有变化就同步,而是等3秒内没有新变化后再触发:
bash
#!/bin/bash SOURCE="/var/www/html/" DEST="backup-server:/var/www/html/" inotifywait -m -r -e modify,create,delete,move --format '%w%f' "$SOURCE" | while read FILE; do echo "[$(date)] 检测到变化: $FILE" # 等待3秒,如果3秒内又有变化,sleep会被后续的读取重置 timeout 3 cat > /dev/null 2>&1 || true rsync -avz --delete "$SOURCE" "$DEST" done
这个脚本利用了管道阻塞的特性:read会等待下一次inotify事件输出,如果3秒内有新事件,cat会被迫提前退出(这不是完美的去抖动方案,但作为Shell脚本的轻量实现已经足够实用)。
更完善的去抖动策略(避免高频同步)、错误重试、日志轮转等功能,可以参考成熟的同步工具如lsyncd(它正是inotify + rsync的封装,已经内置了这些机制)。
六、rsync vs scp vs NFS
| 维度 | rsync | scp | NFS |
|---|---|---|---|
| 增量同步 | ✅ 仅传输变化部分 | ❌ 全量拷贝 | ✅ 文件系统级别 |
| 传输加密 | ✅ 走SSH | ✅ 走SSH | ❌ 需额外配置(Kerberos或VPN) |
| 实时性 | ⚠️ 依赖触发/定时 | ❌ 手动执行 | ✅ 透明实时 |
| 断点续传 | ✅--partial | ❌ 失败后需重新开始 | ✅ 文件系统级别 |
| 适用场景 | 备份、镜像、大文件 | 一次性传输 | 多服务器实时共享 |
七、本篇小结
rsync核心机制:
强弱校验和 + 块匹配 = 只传输变化部分
-avz:归档模式 + 显示详情 + 传输压缩
--delete:精确镜像(小心使用)
-n(--dry-run):先模拟再执行,万能安全网
从定时到实时:
crontab + rsync = 定时备份(最简单)
inotifywait + rsync = 实时同步(变化即触发)
lsyncd = 生产级封装(去抖动 + 错误处理)
动手练习
bash
# 1. 体验rsync增量同步的基本用法 mkdir -p /tmp/source /tmp/dest echo "file1" > /tmp/source/file1.txt echo "file2" > /tmp/source/file2.txt rsync -av /tmp/source/ /tmp/dest/ ls /tmp/dest/ # 确认文件已同步 # 2. 验证增量特性(修改一个文件后再同步,观察传输内容) echo "appended" >> /tmp/source/file1.txt rsync -av /tmp/source/ /tmp/dest/ # 只传输变化部分 # 3. 体验inotifywait inotifywait -m /tmp/source & # 在另一个终端操作 /tmp/source 目录,观察输出 echo "test" > /tmp/source/newfile.txt # 回到第一个终端看变化通知 fg # 把后台的inotifywait调回前台,Ctrl+C结束 # 测试后自行清理:rm -rf /tmp/source /tmp/dest
八、下篇预告
代码写完了,但团队协作中如何管理代码版本、避免“最终版_v3_改2_真的不改了.py”这种混乱?Git是程序员每天都要用的版本控制工具。
下一篇我们将搭建私有Git服务器——使用Gitea(一个轻量级的GitHub替代品),让团队拥有自己的代码托管平台。你将学会Git的基本工作流、搭建Gitea服务、配置SSH方式推送代码。
延伸思考:rsync配合--link-dest参数可以实现增量快照备份——每次备份都创建一个完整的“镜像”,但未变化的文件是通过硬链接指向上一次备份的,实际只占用极少额外空间。macOS的Time Machine和Linux的rsnapshot都基于这个原理。如果你的备份需求增长到需要保留多个历史版本、但磁盘空间有限,不妨研究一下这个参数。
