1. 这不是“实时”,但比实时更可靠:RSYNC 文件同步的真相与实战定位
很多人第一次看到“RSYNC—文件实时同步”这个标题,心里会下意识打个问号:rsync 不是“增量同步工具”吗?它本身根本不具备监听文件变化、触发自动执行的能力,哪来的“实时”?这其实是个长期被误读、被营销话术带偏的概念。我做 Linux 系统运维和数据管道搭建十多年,从早期 NAS 搭建、到后来的多机房备份系统、再到现在混合云环境下的边缘节点数据分发,几乎每天都在和 rsync 打交道。我可以很确定地说:rsync 本身不是实时同步工具,但它却是构建高可靠、低开销、可审计、易排错的准实时同步方案最坚实、最通用、最不可替代的底层引擎。所谓“实时”,在绝大多数生产场景中,指的其实是“秒级延迟内完成变更同步”,而不是毫秒级的事件驱动响应——而 rsync 配合合理的触发机制(如 inotifywait、systemd path unit、或轻量级守护进程),恰恰能在资源占用极低的前提下,稳定达成 1~3 秒的端到端同步延迟。飞牛 NAS 的 rsync 功能之所以被大量用户称为“实时”,正是因为其后台封装了 inotify + rsync 的组合逻辑,并做了路径白名单、冲突处理、失败重试等工程化封装,让用户感知不到底层复杂性。关键词RSYNC和文件实时同步并不矛盾,关键在于理解它的角色:它不是冲锋陷阵的“侦察兵”,而是稳坐后方、精准投送的“后勤补给车队”。你不需要它每毫秒都跑一趟,你需要的是它每次出发都带对货、走对路、不丢件、可追溯。这也是为什么在备份、归档、内容分发、开发环境镜像等场景中,哪怕有 rsync 的替代方案(如 lsyncd、Syncthing、甚至某些商业同步服务),只要涉及跨网络、跨权限、需保留 ACL/SELinux 上下文、或要求严格一致性校验,rsync 依然是工程师兜底的第一选择。
2. 核心设计思路拆解:为什么不用“真实时”,而选“rsync+触发器”组合?
2.1 “实时”的代价:监听机制的本质差异
真正的实时文件同步,依赖的是操作系统内核提供的文件系统事件通知机制,比如 Linux 的 inotify、fanotify,或 macOS 的 FSEvents。这些机制能精确捕获IN_CREATE、IN_MODIFY、IN_MOVED_TO等事件,理论上可以做到毫秒级响应。但问题在于:事件本身不等于同步动作,它只是个“信号”。你收到一个IN_MODIFY事件,只代表某个文件被写入了一次,但你根本不知道这次写入是否已完成(比如大文件分块写入)、是否还有后续操作(比如编辑器先写临时文件再 rename)、甚至这个事件是否可靠(inotify 有队列长度限制,高并发下会丢事件)。我曾经在一个日志采集项目里直接用 inotify 监听/var/log/下数百个文件,结果在流量高峰时,inotify 队列溢出,导致部分日志片段完全丢失,最终不得不回退到轮询+mtime 检查的老办法。而 rsync 的核心价值,恰恰在于它不依赖事件,而是基于状态比对:它每次运行,都会对源和目标进行一次完整的、可验证的状态快照比对(通过文件大小、修改时间、校验和三重判断),然后只传输真正发生变化的块。这种“悲观但确定”的哲学,牺牲了理论上的最低延迟,却换来了极高的数据一致性和故障鲁棒性。
2.2 RSYNC 的不可替代性:不只是“快”,更是“准”与“稳”
很多人以为 rsync 快,是因为它只传差异。这没错,但远不是全部。真正让它在十年间屹立不倒的,是以下四个底层能力:
块级增量(Delta Encoding):rsync 不是简单地比较整个文件,而是将文件切分为固定大小(默认 64KB)的数据块,为每个块计算滚动校验和(Rsync algorithm 中的弱校验 + MD4 强校验)。当目标端已有该块时,源端就跳过传输,只发送块索引。这意味着即使一个 1GB 的视频文件只改了最后 1KB,rsync 也大概率只传几 KB 的元数据和新块,而非整个文件。我实测过一个 2.3GB 的数据库 dump 文件,仅修改末尾一行 SQL,rsync -a 耗时 1.8 秒,传输量仅 42KB;而 scp 全量传输则需 3 分钟以上。
跨协议、跨权限、跨文件系统兼容性:rsync 可以通过 ssh、rsh、甚至本地路径工作。更重要的是,它能原样保留
owner/group、permissions、timestamps、symlinks、hardlinks,甚至xattrs(需加-X参数)。在企业环境中,一个脚本的执行权限、一个配置文件的 SELinux context、一个日志目录的 setgid 位,丢了任何一个都可能引发连锁故障。scp 或 cp 命令无法保证这些元数据的完整迁移,而 rsync 是目前唯一被广泛验证、零配置即可满足全量元数据同步的开源工具。断点续传与失败安全:rsync 的传输是原子性的。它总是在目标端先写入一个临时文件(如
.filename.XXXXXX),待全部数据写入并校验无误后,才rename成目标文件名。这意味着即使同步中途网络中断、磁盘满或进程被 kill,目标端的原始文件始终完好无损,不会出现“半截文件”。我在某次跨省专线升级中,rsync 任务因链路抖动反复中断 7 次,但最终成功完成,且所有文件 md5sum 完全一致——这种“失败即无害”的特性,在无人值守的备份场景中价值千金。可审计、可预测、可调试:每一次 rsync 运行,都可以通过
-v(verbose)、--itemize-changes(详细变更列表)、--stats(统计摘要)输出清晰的日志。你可以精确知道“哪些文件被更新”、“传输了多少字节”、“跳过了多少文件”。而基于 WebSocket 或 P2P 协议的“实时同步”工具,日志往往只显示“连接建立”、“同步完成”,一旦出错,排查如同雾里看花。在金融、医疗等强合规行业,这种可审计性不是加分项,而是准入门槛。
2.3 “实时同步”方案的三层架构:rsync 是承重墙
一个健壮的“文件实时同步”系统,绝不是单靠一个命令就能实现的。它必然包含三个逻辑层:
事件感知层(Trigger Layer):负责监听文件系统变化,生成“需要同步”的信号。这是最易出错的一层。inotifywait 是最常用的选择,但它必须配合合理的
-m(monitor)、-e(events)参数和--format输出格式,否则极易漏事件或产生重复触发。例如,监听create,modify,move,delete四类事件是基础,但必须排除attrib(属性变更)这类高频无意义事件;同时,--exclude排除.swp、.tmp等编辑器临时文件,否则会引发雪崩式同步。调度协调层(Orchestration Layer):负责接收信号、去重、限流、排队,并调用 rsync。这里不能简单地“一事件一 rsync”,否则在批量操作(如
git checkout、rsync自身同步)时,会瞬间发起数十个 rsync 进程,耗尽内存和带宽。成熟的方案会引入队列(如 Redis List)、锁机制(flock)、或退避策略(如sleep 0.5后再检查是否有新事件)。飞牛 NAS 的后台正是在此层做了深度优化,它会合并 2 秒内的所有变更,统一触发一次 rsync,极大降低了系统负载。执行引擎层(Execution Layer):这就是 rsync 本身。它不关心谁触发、为何触发,只专注做好一件事:确保目标端与源端在指定路径下,达到完全一致的状态。它的参数组合就是你的“同步契约”:
-a(archive mode,等价于-rlptgoD)是底线,-v用于调试,--delete用于双向清理,--exclude用于过滤,-z(压缩)用于广域网,-e "ssh -p 2222"用于非标端口。这一层越纯粹,整个系统就越稳定。
把 rsync 当作“实时同步”的全部,就像把发动机当成整辆汽车——它强大,但没有底盘、没有方向盘、没有刹车,你哪儿也去不了。理解这三层分工,是设计任何 rsync 同步方案的前提。
3. 核心细节解析与实操要点:从命令到生产级脚本
3.1 RSYNC 最常用参数的“人话”解读与陷阱
rsync -av是新手入门第一课,也是老手日常最常敲的命令。但a和v背后藏着太多容易踩坑的细节,必须掰开揉碎讲清楚:
-a(archive):这是 rsync 的灵魂参数,它不是简单的“递归复制”,而是一组预设开关的集合。展开来看,它等价于:-r(recursive):递归进入子目录。注意:它不会同步空目录,除非加上-d(dirs)或-f 'dir'。-l(links):保留符号链接(symlinks),即目标端也会是一个指向相同路径的软链接。但如果源链接指向一个不存在的路径,目标端链接也会失效——rsync 不会帮你修复链接目标。-p(permissions):保留文件权限(mode bits)。这是chmod的镜像,但要注意:如果目标文件系统是 FAT32 或 exFAT(如 U 盘),它根本不支持 Unix 权限,此时-p会静默失败,文件权限会变成默认值(通常是 644/755)。-t(times):保留修改时间(mtime)和访问时间(atime)。mtime对备份至关重要,因为 rsync 默认只比对mtime和 size 来判断是否需要同步。但atime在现代 Linux 上默认是relatime模式,频繁更新会带来 I/O 开销,生产环境常禁用atime(mount -o noatime),所以-t实际上主要保mtime。-g(group)和-o(owner):保留所属组和所有者。这是最大陷阱!rsync 默认以当前用户身份运行,它只能设置目标文件的 owner/group 为你当前用户或其所属组。如果你想把文件同步成www-data:www-data,你必须以www-data用户身份运行 rsync,或者在目标端提前创建好同名用户/组。否则,-g -o会静默忽略,文件 owner 会变成执行 rsync 的用户。-D:保留设备文件(device files)和特殊文件(character & block devices)。普通用户几乎用不到,但在同步/dev或容器 rootfs 时是必需的。
提示:
-a不包含-H(hard links)和-X(extended attributes)。如果你的源数据包含硬链接(如某些备份软件生成的 deduped 数据),必须显式加-H,否则硬链接会被复制成独立文件,浪费空间。-X则用于 SELinux context 或 macOS 的 resource fork,不加则丢失。
-v(verbose):显示详细过程。但要注意,-v本身不输出“同步了什么”,它只显示 rsync 正在扫描的目录和文件名。要看到具体哪些文件被更新、删除、跳过,必须加--itemize-changes(简写-i)。-i的输出像这样:>f+++++++++ /path/to/file,其中第一个字符>表示“传输方向(> = to remote)”,第二个f表示“文件(file)”,后面+表示“新增”,.表示“未变”,c表示“内容变更”,h表示“硬链接”,s表示“大小不同”,t表示“时间戳不同”。掌握这个编码,比看一百行日志都管用。--delete:危险但必要的“清理开关”
这个参数让 rsync 在同步完成后,删除目标端存在但源端已不存在的文件。它是实现“镜像同步”的关键,但也是一把双刃剑。常见错误是直接写rsync -av --delete /src/ user@host:/dst/,这会导致/dst/下所有不在/src/中的文件被清空!正确姿势是:- 永远使用 trailing slash:
/src/(带斜杠)表示同步/src/下的所有内容;/src(不带斜杠)表示同步/src这个目录本身。前者是“内容同步”,后者是“目录同步”。--delete只对trailing slash的源路径生效,且只删除目标路径下与源路径“对应层级”的多余文件。 - 务必配合
--dry-run(-n)先测试:在首次启用--delete前,一定要加-n运行一次,观察它打算删除哪些文件。我曾因忘记-n,在测试环境误删了客户一个月的报表数据,那次教训让我养成了“--delete前必-n”的肌肉记忆。 - 考虑
--delete-after:默认--delete在传输前执行,可能导致目标端短暂出现“空目录”。--delete-after会在所有文件传输完成后才删除,更安全,但会多占用一点磁盘空间。
- 永远使用 trailing slash:
3.2 SSH 密钥与连接优化:让 rsync 跑得又稳又快
rsync 默认通过 ssh 传输,因此 SSH 配置直接影响同步效率和稳定性:
免密登录是前提:
rsync -av -e "ssh -i /path/to/key" ...是标准写法。但更优雅的方式是配置~/.ssh/config:Host myserver HostName 192.168.1.100 User backupuser IdentityFile ~/.ssh/backup_key Port 2222 ServerAliveInterval 60 ServerAliveCountMax 3这样,
rsync -av /src/ myserver:/dst/就能自动使用指定密钥、端口和保活设置。ServerAliveInterval是救命参数——它让 ssh 客户端每隔 60 秒发一个心跳包,避免 NAT 设备或防火墙因超时关闭长连接。没有它,大文件同步到一半断连是家常便饭。压缩与带宽限制:
-z参数开启 ssh 层压缩,对文本、日志、代码等可压缩内容效果显著,但对已压缩的图片、视频、zip 包反而增加 CPU 开销。我的经验是:局域网内一律不加-z;广域网(WAN)且带宽紧张时,加-z;若 CPU 较弱(如树莓派),宁可牺牲带宽也不加-z。带宽限制用--bwlimit=KBPS,例如--bwlimit=1000限制为 1MB/s,避免 rsync 吃光所有带宽影响其他业务。超时与重试:rsync 本身没有内置超时,但可以通过 ssh 控制:
rsync -av --timeout=300 --contimeout=30 \ -e "ssh -o ConnectTimeout=30 -o ServerAliveInterval=15" \ /src/ user@host:/dst/--timeout=300是 rsync 级别的整体超时(秒),--contimeout=30是单个文件传输超时。ConnectTimeout是 ssh 连接建立超时,ServerAliveInterval是连接保活间隔。这组参数组合,能有效防止 rsync 在网络抖动时无限挂起。
3.3 生产级同步脚本:一个可直接抄作业的模板
下面是一个我在线上环境跑了三年、零故障的 rsync 同步脚本(sync_to_backup.sh),它融合了触发、限流、日志、错误处理等所有关键要素:
#!/bin/bash # 同步脚本:从 /data/web/ 到 backup-server:/backup/web/ # 作者:一线运维老炮 | 日期:2024年更新 # ========== 配置区 ========== SOURCE_DIR="/data/web/" DEST_HOST="backup-server" DEST_DIR="/backup/web/" RSYNC_OPTS="-av --delete-after --exclude='*.log' --exclude='.git/' --exclude='cache/'" LOG_FILE="/var/log/rsync_web_sync.log" LOCK_FILE="/tmp/rsync_web_sync.lock" MAX_RETRY=3 RETRY_DELAY=10 # ========== 函数定义 ========== log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" } # 获取锁,失败则退出 acquire_lock() { if ! (set -o noclobber; : > "$LOCK_FILE") 2> /dev/null; then log "ERROR: Another instance is running. Exiting." exit 1 fi # 设置锁文件的清理钩子 trap 'rm -f "$LOCK_FILE"' EXIT } # 执行 rsync,带重试 run_rsync() { local attempt=1 while [ $attempt -le $MAX_RETRY ]; do log "INFO: Starting rsync (attempt $attempt)..." if rsync $RSYNC_OPTS \ --itemize-changes \ --stats \ -e "ssh -o ConnectTimeout=30 -o ServerAliveInterval=15" \ "$SOURCE_DIR" "$DEST_HOST:$DEST_DIR" >> "$LOG_FILE" 2>&1; then log "SUCCESS: rsync completed successfully." return 0 else log "WARN: rsync failed on attempt $attempt. Retrying in $RETRY_DELAY seconds..." sleep $RETRY_DELAY ((attempt++)) fi done log "FATAL: rsync failed after $MAX_RETRY attempts. Giving up." exit 1 } # ========== 主流程 ========== acquire_lock run_rsync关键设计说明:
锁机制(
flock替代方案):这里用noclobber重定向创建锁文件,比flock更轻量,且无需额外依赖。trap 'rm -f "$LOCK_FILE"' EXIT确保无论脚本如何退出(成功、失败、被 kill),锁文件都会被清理,避免死锁。重试逻辑:
MAX_RETRY=3和RETRY_DELAY=10是经过大量实践验证的平衡点。重试太少,偶发网络抖动就失败;重试太多,可能掩盖真实故障。10 秒延迟足够让网络恢复,又不会让任务积压太久。日志结构化:每条日志都带时间戳,且明确标注
INFO/WARN/ERROR/FATAL级别。--itemize-changes和--stats的输出直接追加到日志,便于事后审计。你可以用grep '^\>' $LOG_FILE | wc -l快速统计本次同步了多少文件。排除规则:
--exclude='*.log'是典型场景——日志文件频繁写入,同步它们毫无意义,还可能因文件被占用导致 rsync 失败。--exclude='.git/'排除 Git 元数据,--exclude='cache/'排除缓存目录,这些都是“脏数据”,不该进入备份。
把这个脚本保存为/usr/local/bin/sync_to_backup.sh,chmod +x,再配合inotifywait触发,就是一个完整的准实时同步系统。
4. 实操过程与核心环节实现:从零搭建一个飞牛风格的同步服务
4.1 环境准备与依赖安装
我们以 Ubuntu 22.04 服务器为例,目标是搭建一个类似飞牛 NAS 的“监听/srv/www/目录,变更后 2 秒内同步到远程备份服务器”的服务。
步骤 1:安装核心组件
# 更新源并安装 rsync(通常已预装)和 inotify-tools sudo apt update sudo apt install -y rsync inotify-tools # 验证安装 rsync --version # 应显示 3.2.x 或更高 inotifywait --version # 应显示 3.22.x 或更高步骤 2:配置 SSH 免密登录在主服务器(源端)上:
# 生成专用密钥(不设密码,专用于自动化) ssh-keygen -t ed25519 -f ~/.ssh/rsync_backup_key -N "" # 将公钥复制到备份服务器(目标端) ssh-copy-id -i ~/.ssh/rsync_backup_key.pub backupuser@backup-server # 测试连接(应无需密码) ssh -i ~/.ssh/rsync_backup_key -o ConnectTimeout=5 backupuser@backup-server "echo OK"步骤 3:创建同步用户与目录在备份服务器(目标端)上:
# 创建专用用户,禁止 shell 登录,提高安全性 sudo adduser --disabled-password --gecos "" --shell /usr/sbin/nologin backupuser # 创建备份目录并授权 sudo mkdir -p /backup/www sudo chown backupuser:backupuser /backup/www sudo chmod 755 /backup/www注意:
--shell /usr/sbin/nologin是关键安全措施。这个用户只能用于 rsync 传输,无法交互式登录,极大缩小了攻击面。很多教程教人用 root 用户同步,这是严重安全隐患。
4.2 编写 inotify 监听与触发脚本
创建/usr/local/bin/watch_and_sync.sh:
#!/bin/bash # 监听脚本:监控 /srv/www/,变更后触发 rsync MONITOR_DIR="/srv/www/" SYNC_SCRIPT="/usr/local/bin/sync_to_backup.sh" # 日志文件 LOG_FILE="/var/log/inotify_watch.log" # 记录启动时间 echo "[$(date)] Watcher started for $MONITOR_DIR" >> "$LOG_FILE" # 使用 inotifywait 监听,-m 表示持续监控,-e 指定事件类型 # --format '%w%f %e' 输出 "完整路径 事件类型",如 "/srv/www/index.html CREATE" inotifywait -m -e create,modify,move,delete,attrib \ --exclude '\.(swp|swo|tmp|log)$' \ --format '%w%f %e' \ "$MONITOR_DIR" | while read file event; do # 过滤掉编辑器临时文件和日志文件 if [[ "$file" =~ \.(swp|swo|tmp|log)$ ]] || [[ "$event" == "ATTRIB" ]]; then continue fi # 记录事件 echo "[$(date)] Event: $event on $file" >> "$LOG_FILE" # 为防止单次操作(如 git pull)触发大量事件,引入 2 秒冷却期 # 方法:touch 一个标记文件,然后 sleep 2 秒,再检查标记是否仍存在 # 如果存在,说明这是本轮第一个事件,执行同步;否则跳过 MARKER="/tmp/rsync_trigger_marker" if [ ! -f "$MARKER" ]; then touch "$MARKER" # 启动后台同步任务,避免阻塞 inotifywait nohup "$SYNC_SCRIPT" > /dev/null 2>&1 & # 2 秒后删除标记 (sleep 2; rm -f "$MARKER") & fi done关键点解析:
事件过滤:
--exclude '\.(swp|swo|tmp|log)$'正则表达式排除 vim 临时文件(.swp)、PHPStorm 临时文件(.swo)、通用临时文件(.tmp)和日志文件(.log)。ATTRIB事件(如chmod)被continue跳过,因为它不改变文件内容,无需同步。去重与限流:核心思想是“标记+冷却”。第一次事件到来,创建
marker文件并立即触发同步脚本;后续事件在 2 秒内到达,发现marker已存在,就跳过。2 秒后marker被自动删除,下一轮监听开始。这比简单sleep 2更高效,因为inotifywait本身是阻塞的,sleep会卡住整个监听循环。后台执行:
nohup "$SYNC_SCRIPT" > /dev/null 2>&1 &将同步脚本放入后台运行,确保inotifywait循环不被阻塞。nohup防止终端关闭导致进程终止。
4.3 服务化:用 systemd 管理监听进程
让脚本开机自启、崩溃自恢复,必须用 systemd:
创建/etc/systemd/system/rsync-watcher.service:
[Unit] Description=RSYNC File Watcher Service After=network.target [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/srv/www ExecStart=/usr/local/bin/watch_and_sync.sh Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=rsync-watcher # 安全加固 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 [Install] WantedBy=multi-user.target启用并启动服务:
# 重新加载 systemd 配置 sudo systemctl daemon-reload # 启用开机自启 sudo systemctl enable rsync-watcher.service # 启动服务 sudo systemctl start rsync-watcher.service # 查看状态(应显示 active (running)) sudo systemctl status rsync-watcher.service # 查看实时日志 sudo journalctl -u rsync-watcher.service -f安全参数详解:
ProtectSystem=strict:将/usr,/boot,/etc挂载为只读,防止脚本意外修改系统文件。ProtectHome=true:将/home,/root,/run/user挂载为不可访问,隔离用户数据。RestrictAddressFamilies=...:只允许脚本使用 Unix socket、IPv4、IPv6,禁用其他协议(如 Netlink),大幅缩小攻击面。
这些参数是生产环境的标配,不是可选项。
4.4 验证与压力测试
基础验证:
在/srv/www/下创建一个测试文件:
echo "test content $(date)" > /srv/www/test.txt等待 2~3 秒,检查备份服务器:
ssh backupuser@backup-server "ls -la /backup/www/test.txt" # 应看到文件存在,且时间戳与源端一致压力测试(模拟真实场景):
# 在源端快速创建 100 个文件 for i in {1..100}; do echo "file $i" > /srv/www/file$i.txt; done # 观察日志 sudo journalctl -u rsync-watcher.service --since "1 minute ago" | grep "Event\|SUCCESS" # 应看到大量 `CREATE` 事件,但 `rsync` 只执行了一次(因为都在 2 秒冷却期内) # 检查同步结果 ssh backupuser@backup-server "ls /backup/www/ | wc -l" # 应为 100故障注入测试:
- 拔掉网线 10 秒,再插回:
rsync应在重试后自动恢复。 - 手动删除备份服务器上的一个文件:下次同步时,
--delete-after应将其恢复(因为源端仍有)。 - 在源端
chmod 777 /srv/www/test.txt:目标端权限应保持不变(因为rsync -a会保留原权限,且www-data用户无权更改backupuser的文件权限)。
5. 常见问题与排查技巧实录:那些年踩过的坑
5.1 “同步了,但文件没变?”——时间戳与精度陷阱
现象:
你修改了源文件内容,rsync -av却显示skipping,目标文件内容没更新。
原因与排查:
rsync 默认只比对mtime(修改时间)和文件大小。Linux 的mtime精度是秒级。如果你在 1 秒内连续修改同一个文件两次(比如编辑器自动保存),第二次的mtime可能和第一次一样,rsync 就认为“没变”。
验证:
stat /path/to/file # 查看 Access/Modify/Change 时间戳解决方案:
- 加
--checksum(-c)参数:强制对每个文件计算 MD5 校验和进行比对。这能 100% 确保内容一致,但代价是:每次同步都要读取所有文件内容,I/O 和 CPU 开销剧增。仅在小文件、低频变更场景下使用。 - 调整文件系统挂载选项:某些文件系统(如 ext4)支持
relatime或strictatime。relatime会减少atime更新频率,但不影响mtime。mtime精度由内核决定,无法通过挂载选项提升。 - 接受现实:对于绝大多数 Web 内容、文档、代码,秒级精度完全够用。
--checksum是“银弹”,但不应成为默认。
5.2 “Permission denied” —— 权限与用户上下文迷宫
现象:rsync -av /src/ user@host:/dst/报错rsync: failed to set times on "/dst/file": Operation not permitted (1)或rsync: chown "/dst/file" failed: Operation not permitted (1)。
原因与排查:
这不是 rsync 的 bug,而是 Linux 权限模型的体现。chown和touch(设置时间戳)是特权操作,普通用户只能对自己拥有的文件执行。当你以userA身份运行 rsync,它试图把目标文件的 owner 设为userB,而userA没有CAP_CHOWN能力,就会失败。
解决方案:
- 方案 A(推荐):目标端用 root 用户接收,再
chown
在目标端/etc/rsyncd.conf中配置模块,uid = root,gid = root,然后rsync -av --rsync-path="sudo rsync"。但这需要配置 sudoers,增加复杂度。 - 方案 B(最常用):源端和目标端用同一用户
确保user@host在目标端拥有/dst/目录的完全控制权(chown -R user:user /dst/),并且rsync -a的-o -g参数才能生效。这是最简单、最安全的做法。 - 方案 C:放弃
-o -g,只保留-p -t
如果 owner/group 不重要,就不要加-o -g。rsync -rltvp是更安全的组合,它保留权限和时间戳,但不尝试更改所有者。
5.3 “同步太慢!”——网络与 I/O 瓶颈定位
现象:
同步一个 10GB 目录,预计 5 分钟,结果花了 40 分钟。
排查四步法:
- 测网络带宽:
iperf3 -c backup-server,确认实际带宽是否达标。如果只有 10MB/s,那 rsync 再怎么优化也快不了。 - 测磁盘 I/O:
iostat -x 1,观察%util(设备利用率)和await(平均等待时间)。如果%util接近 100%,await> 50ms,说明磁盘是瓶颈。可能是目标端磁盘老旧,或源端在 RAID 5 上做大量小文件读取。 - 看 rsync 统计:加
--stats参数,关注Number of files、Number of files transferred、Total file size、Total transferred file size。如果transferred接近total,说明是全量传输,不是增量问题;如果transferred很小但耗时很长,说明是 I/O 或网络延迟问题。 - 检查 SSH 加密开销:
rsync -av -e "ssh -o Cipher=arcfour256"(ARC4 是最快的 SSH 加密算法,但安全性较低,仅用于内网)。如果速度提升明显,说明是 CPU 加密瓶颈。
终极提速技巧:
- 源端用
--compress-level=1:比-z更细粒度控制压缩强度。 - 目标端用
--partial-dir=.rsync-partial:将未完成的文件暂存到临时目录,避免干扰正常文件,也方便断点续传。 - 大文件单独处理:对 >100MB 的文件,用
rsync --whole-file(禁用块级增量),因为计算校验和的开销可能超过传输节省的带宽。
5.2 “--delete 删错了!”——安全同步的黄金法则
现象:
启用