前言

数据要在两台机器间保持同步——备份、分发、迁移——十几年过去了,答案依然是 rsync:增量传输、断点续传、保持属性、走 SSH。配合 inotify 触发,还能做到准实时同步。

一、rsync 核心概念

增量算法:两端比对块校验和,只传差异部分——同步 100GB 的目录,改动 10MB 就只传 10MB。

源 ──►(差异计算)──► 目标
       滚动校验和比对, 只传变化的块

三种工作模式:

# 1. 本地同步
rsync -av /data/src/ /data/backup/

# 2. 远程(走 SSH, 最常用)
rsync -av /data/src/ user@backup:/data/backup/

# 3. 守护进程模式(rsyncd, 大量数据/免交互)
rsync -av /data/src/ rsync://backupHost/mod-backup/

二、必懂参数与斜杠陷阱

rsync -avz --delete /data/src/ backup:/data/backup/
参数 含义
-a 归档模式(递归+保持权限/时间/属主/软链)
-v 显示过程
-z 传输时压缩(文本目录很值; 已压缩文件别开)
--delete 删除目标端多余文件(镜像同步; 生产慎用!)
-P 显示进度+断点续传(= –partial –progress)
--exclude 排除(可多次)
-n / --dry-run 演练模式(只说要做啥, 不真做)
--bwlimit=10240 限速 10MB/s(避开业务高峰)

斜杠决定语义(新手第一大坑):

rsync -av /data/src  /backup/     # → /backup/src/...   同步"src目录本身"
rsync -av /data/src/ /backup/     # → /backup/...       同步"src里的内容"

排错利器 dry-run:

# 永远先演练, 尤其带 --delete 时!
rsync -avn --delete /data/src/ backup:/data/backup/ | head

三、实战:每日增量备份

#!/usr/bin/env bash
# /opt/scripts/backup-data.sh
set -euo pipefail

SRC="/data/app/"
DEST="backup@10.0.2.50:/backup/app-$(hostname)/"
LOG="/var/log/rsync-backup.log"

exec >>"$LOG" 2>&1
echo "=== $(date '+%F %T') 备份开始 ==="

rsync -az --delete \
  --exclude="*.tmp" \
  --exclude="cache/" \
  --bwlimit=51200 \
  -e "ssh -p 22 -i /home/backup/.ssh/id_ed25519" \
  "$SRC" "$DEST"

echo "=== 备份完成, 同步 $(du -sh $SRC | cut -f1) ==="

配合 cron(每日 2 点):

# /etc/cron.d/data-backup
0 2 * * * backup /opt/scripts/backup-data.sh

镜像型备份的缺陷:源被误删/中勒索,--delete 会把备份也同步删掉!必须配合快照或版本化:

# 版本化: 每天一个硬链接快照目录(rsync --link-dest 神器)
TODAY=/backup/app/$(date +%F)
YESTERDAY=/backup/app/$(date -d yesterday +%F)

rsync -a --delete --link-dest="$YESTERDAY" /data/app/ "$TODAY/"
# 未变化的文件 = 硬链接(0额外空间), 变化的文件 = 新副本
# 效果: 每天完整视图 + 增量磁盘占用

四、inotify:实时触发同步

rsync 本身是"轮询全量比对",inotify 让文件一变化就触发:

dnf install -y inotify-tools

# 监控目录事件
inotifywait -mrq /data/web \
  -e create,modify,delete,move,close_write \
  --format '%w%f %e' | while read -r file event; do
    echo "[$event] $file"
done

组合成实时同步服务:

#!/usr/bin/env bash
# /opt/scripts/realtime-sync.sh(用 systemd 常驻)
set -euo pipefail
SRC=/data/web/
DEST=web@10.0.2.60:/data/web/
LOCK=/var/run/realtime-sync.lock

# 防重入
exec 9>"$LOCK"; flock -n 9 || exit 0

# 先做一次全量基线
rsync -a --delete "$SRC" "$DEST"

# 监听变化, 攒 2 秒批量同步(避免高频触发)
inotifywait -mrq "$SRC" -e create,modify,delete,move,close_write --format '%w%f' |
while read -r _; do
    sleep 2
    rsync -a --delete "$SRC" "$DEST"
done
# /etc/systemd/system/realtime-sync.service
[Unit]
Description=Realtime sync /data/web
After=network-online.target

[Service]
ExecStart=/opt/scripts/realtime-sync.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

inotify 的坑:

问题 处理
大目录报队列溢出 sysctl fs.inotify.max_user_watches=524288
极高频写入刷爆 rsync 批量节流(如上 sleep);或改 lsyncd
只适合单向 双向同步不要双机互 rsync(会脑裂),要双向用 syncthing/unison

lsyncd:lua 封装的 inotify+rsync,配置化更优雅:

-- /etc/lsyncd.conf.lua
settings { logfile = "/var/log/lsyncd.log", statusFile = "/var/log/lsyncd.status" }
sync {
    default.rsync,
    source    = "/data/web",
    target    = "web@10.0.2.60:/data/web",
    delay     = 3,
    rsync     = { archive = true, delete = true }
}

五、rsync 守护进程模式(免 SSH 大带宽场景)

# /etc/rsyncd.conf(服务端, 备份机)
uid = backup
gid = backup
use chroot = yes
max connections = 10

[mod-backup]
    path = /backup/app
    comment = app backup
    read only = false
    auth users = syncer
    secrets file = /etc/rsyncd.secrets    # 格式: syncer:密码, 权限600
systemctl enable --now rsyncd
# 客户端
rsync -az --password-file=/etc/rsync.pass /data/app/ \
  rsync://syncer@10.0.2.50/mod-backup/

六、同步方案选型表

需求 方案
每日备份 cron + rsync -a + –link-dest 版本化
准实时单向 inotify+rsync / lsyncd
双向同步 syncthing / unison
海量小文件 rsync 排除临时 + --whole-file 局域网加速
跨机房/公网 rsync over SSH 或对象存储同步工具
数据库 别直接 rsync 活库!逻辑备份(xtrabackup/mysqldump)后同步

七、排障速查

# 权限错误
rsync: chown ... failed: Operation not permitted
→ 目标端无 root; 去掉属主保留(-a 里去掉 -o -g: 用 -rlptD)

# 速度慢
→ 看是否走了压缩大文件; 局域网加 --whole-file; 公网加 -z
→ iostat 看磁盘是否瓶颈; --bwlimit 反向确认没被限速

# @ERROR: auth failed
→ secrets 文件权限600; 用户名密码对齐服务端

# inotify 无输出
→ 内核 watches 上限; 监控路径是否挂载点(跨文件系统要分层监控)

小结

场景 命令要点
日常同步 rsync -avz --exclude 注意结尾斜杠
镜像同步 --delete 必先 dry-run
备份保护 --link-dest 版本化快照
实时单向 inotifywait + flock + 批量节流
常驻服务 systemd Restart=always

本文是「Linux 运维」系列第 15 篇。