rsync + cron 远程备份卡住的根本原因是 SSH 缺失保活与超时配置,导致网络抖动时挂起;应添加 -e "ssh -o ConnectTimeout=10 -o ServerAliveInterval=15 -o ServerAliveCountMax=3" 并用 flock 防并发、校验远端 rsync 可执行、避免 --compress 误用。
为什么 rsync + cron 在远程备份时经常卡住或超时
根本原因不是 cron 本身,而是 rsync 默认用 ssh 传输时未设置连接保活和超时策略,遇到网络抖动就挂住,等十几秒甚至几分钟才失败,直接拖垮整个定时任务队列。尤其在跨公网、走 nat 或中间有状态防火墙的链路上,ssh 连接容易被静默断开。
实操建议:
- 给
rsync加上-e "ssh -o ConnectTimeout=10 -o ServerAliveInterval=15 -o ServerAliveCountMax=3",强制控制握手与心跳 - 避免在
crontab里直接写长命令,改用封装好的脚本(如/usr/local/bin/backup-rsync.sh),方便加日志和错误退出判断 - 别依赖
rsync --delete在弱网下跑全量同步,先用--dry-run测一次延迟波动范围
如何让 rsync 只传变化块、不重传整文件
rsync 默认就是基于 delta 的,但前提是服务端也装了 rsync(不是只靠 SSH),且没被误配成 rsync over ssh 却禁用了远程 shell 的 rsync 调用。
常见错误现象:rsync: connection unexpectedly closed 或日志里反复出现 sent N bytes received M bytes 但实际文件大小没变——说明它退化成了 scp 式整传。
检查点:
- 确认远端执行
which rsync有输出,且路径在sshd_config的ForceCommand或AllowUsers白名单里没被拦截 - 本地命令显式指定
--rsync-path="/usr/bin/rsync",防止远端调用错版本或找不到 - 加
--partial --progress观察是否真在传差异块;如果进度条从 0% 开始刷大文件,大概率是协议协商失败退化了
定时任务里怎么避免多实例并发冲突
cron 不保证前一个任务结束才触发下一个,尤其当备份因网络延迟变慢,很容易叠加运行,导致磁盘 I/O 暴涨、rsync 报 failed to open lock file 或覆盖进行中的临时文件。
简单可靠的防重手段:
- 用
flock -n /tmp/backup.lock -c "rsync ..."包裹主命令,获取不到锁就直接退出,不排队不等待 - 别用
date命名备份目录(如backup_$(date +%Y%m%d)),改用$(date -d @$(stat -c %Y /tmp/backup.lock 2>/dev/null || echo $(date +%s)) +%Y%m%d)这类基于锁文件时间戳的方式,确保同一天只保留一份有效快照 - 在脚本开头加
set -e和trap 'rm -f /tmp/backup.lock' EXIT,异常时也能清理锁
为什么加了 --compress 反而更慢还占 CPU
--compress 只对文本类小文件(日志、配置)有效,对已压缩过的文件(.zip、.jpg、.mp4)不仅压不出体积,还会白耗 CPU,且加密通道(SSH)本身已有 zlib 压缩,双压反而负优化。
真实场景建议:
- 删掉
--compress,除非你明确知道源端全是未压缩纯文本,且带宽远小于 CPU 性能(比如树莓派推日志到 VPS) - 想减流量,优先用
--exclude='*.log.*' --exclude='/tmp/*'过滤掉无意义的大文件 - 用
ionice -c 3 nice -n 19 rsync ...降低 I/O 和 CPU 优先级,避免干扰线上服务
最常被忽略的是:备份窗口期和网络质量不匹配。哪怕参数全对,如果把任务塞在凌晨 2 点——而你的云服务器出口刚好在那个时段限速或丢包率飙升——再好的配置也白搭。得先用 mtr -r -c 10 your-backup-host 定期采样,挑出真正稳定的时段再调度。










