做站长做得越久,越明白一个道理:数据比服务器值钱。服务器挂了可以重装,程序丢了可以重新部署,但几年的文章、评论、数据库,一旦没了就真的没了。很多个人站长都做过备份,但备份文件就放在服务器本地,真遇到磁盘损坏、机房故障、被入侵删库,本地备份跟着一起完蛋。这篇文章分享一套实用的 rsync 异地备份方案,把数据备份到另一台机器上,作为数据安全的最后一道防线。
一、为什么说单机备份不算备份
先想一个问题:你的备份和原始数据在同一块硬盘上,硬盘坏了,备份还有吗?没有。再想一个问题:你的服务器被入侵,攻击者拿到权限后第一件事往往是删库或者加密勒索,备份文件就在旁边,能幸免吗?大概率不能。这就是"异地备份"的价值:把数据的副本放到一个物理上、逻辑上都独立的地方。哪怕服务器彻底报废,数据依然安然无恙。
对个人站长来说,异地备份最简单的实现方式就是一台便宜的备份服务器,加上 rsync 这个经典工具。rsync 是 Linux 自带的文件同步工具,核心优势是增量传输:第一次全量同步,之后每次只传输变化的部分,省流量、省时间、省磁盘。
二、rsync 基础:从本地同步开始理解
rsync 的基本语法是 rsync [选项] 源 目标。最常用的组合是 -avz:a 是归档模式,保留权限、属主、时间戳等属性;v 是显示过程;z 是传输时压缩。本地同步示例:
# 把 /data/www 整个目录同步到 /backup/www(注意源目录结尾的斜杠) rsync -avz /data/www/ /backup/www/
源目录结尾带不带斜杠,含义完全不同,这是新手最容易踩的坑:带斜杠表示同步目录里的内容,不带斜杠表示把目录本身也同步过去,目标路径下会多出一层目录。另外 rsync 默认不会删除目标端多余的文件,需要加 --delete 参数,让目标端和源端保持一致:
rsync -avz --delete /data/www/ /backup/www/
注意:--delete 是危险参数,一定要确认源路径写对了再使用,否则源目录一空,目标目录会被跟着清空。
三、异地同步:SSH 传输与免密登录
rsync 支持通过 SSH 协议传输到远程服务器,语法就是在目标路径前加上 用户名@主机:。但 crontab 定时任务不能交互输入密码,所以要先配置 SSH 免密登录:
# 在本地服务器上生成密钥(没有的话) ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519 # 把公钥拷贝到备份服务器 ssh-copy-id -i ~/.ssh/id_ed25519.pub backup_user@备份服务器IP
配置好之后,异地同步命令就是:
rsync -avz --delete /data/www/ backup_user@备份服务器IP:/backup/www/ rsync -avz --delete /data/mysql/ backup_user@备份服务器IP:/backup/mysql/
如果数据库在运行,直接 rsync 数据目录得到的备份可能是不一致的(写了一半的文件)。稳妥的做法是用 mysqldump 先导出成逻辑备份文件,再同步导出文件:
mysqldump --single-transaction -u root -p密码 数据库名 > /backup_tmp/db_$(date +%F).sql rsync -avz /backup_tmp/ backup_user@备份服务器IP:/backup/db/
另外建议给备份服务器上的 sshd 做一些加固:禁用密码登录、更换默认端口、限制只允许备份专用密钥,防止备份服务器本身成为突破口。
四、备份策略设计:快照轮转与保留
如果每天都往备份服务器同步,目标目录里就会有无限增多的文件,旧版本被 --delete 清掉,想找回昨天的误删文件都做不到。更合理的做法是"带历史的轮转备份":最近 7 天每天一份,最近 4 周每周一份,更早的可以丢弃。手动实现可以用硬链接方案:
# 先把当前数据同步到带日期的目录
rsync -avz --delete /data/www/ backup_user@备份机:/backup/www/$(date +%F)/
# 清理策略:保留最近 14 天的快照,其余删除
find /backup/www -maxdepth 1 -type d -name '20*' -mtime +14 -exec rm -rf {} \;更进一步可以用 rsync 的 --link-dest 配合硬链接做增量快照:每次同步时,把上一次的目录作为参照,没有变化的文件通过硬链接复用,相同内容只占一份磁盘空间,效果类似专业的备份软件,但实现只需要一条命令:
rsync -avz --delete --link-dest=/backup/www/昨天 /data/www/ backup_user@备份机:/backup/www/今天
这样每个快照看起来都是完整的一份数据,但磁盘占用只有第一份加上每天的变化量。对个人站长来说,如果觉得轮转脚本复杂,用简单的"保留最近 N 份"策略也已经够用,先把备份跑起来,再逐步优化。
五、自动化:定时任务与结果通知
手动备份永远坚持不下去,一定要交给定时任务。把备份逻辑写成一个脚本,每天凌晨执行:
#!/bin/bash # /usr/local/bin/daily_backup.sh set -e LOG=/var/log/backup.log echo "===== $(date) 备份开始 =====" >> $LOG mysqldump --single-transaction -u root 数据库名 | gzip > /backup_tmp/db.sql.gz rsync -avz --delete /data/www/ backup_user@备份机:/backup/www/ >> $LOG 2>&1 rsync -avz --delete /backup_tmp/ backup_user@备份机:/backup/db/ >> $LOG 2>&1 echo "备份完成" >> $LOG
然后在 crontab 里加一行:
0 3 * * * /usr/local/bin/daily_backup.sh
选择凌晨 3 点这类低峰时段执行,避开访问高峰。脚本里 set -e 保证任何一步失败就立即退出,配合日志可以快速定位问题。如果备份失败没有任何通知,等于没有备份——建议在脚本里加上失败时发送邮件或者推送通知的逻辑,实在不行,每天瞄一眼日志文件也行。
六、恢复演练:备份最终要能还原
备份做得再勤,没演练过的备份都不算数。很多站长在真正需要恢复时才发现:备份文件是坏的、解压密码忘了、数据库版本不兼容。所以强烈建议每季度做一次完整的恢复演练:在一台干净的机器上,用备份把网站完整恢复一遍,验证文件权限、数据库导入、站点访问都正常。
恢复的核心命令很简单:
# 文件恢复 rsync -avz backup_user@备份机:/backup/www/ /data/www/ # 数据库恢复 gunzip -c db.sql.gz | mysql -u root 数据库名
演练的意义在于提前暴露问题。演练通过之后,把恢复步骤整理成文档,和备份放在一起。真到出事那天,照着文档一步步执行,比临时回忆要可靠得多。
七、备份检查清单:你的方案合格吗
搭好 rsync 异地备份之后,可以用下面这份清单做一次自查,缺哪项补哪项:
- 数据库是否每天自动备份,并且备份文件同步到了异地?
- 网站文件是否每周至少备份一次,且保留了最近两周以上的历史版本?
- 备份是否存放在与生产服务器物理隔离的另一台机器或对象存储上?
- 备份任务失败时是否有日志可查、有通知可收,而不是默默失败?
- 是否每季度做过一次完整的恢复演练,确认备份真的能还原网站?
- MySQL 的 binlog 是否开启,能否配合备份恢复到任意时间点?
这六条都做到,你的备份体系在个人站长这个级别已经算优秀了。如果还有没做到的,建议按优先级补齐:异地存放和恢复演练最重要,其次是自动化与失败通知,最后才是快照轮转这些锦上添花的功能。
八、常见问题 FAQ
问:没有第二台服务器怎么办? 可以用对象存储替代,比如各大云厂商的 OSS/COS,用 rclone 之类的工具把备份传上去,成本很低,效果类似异地备份。
问:备份数据要不要加密? 如果备份服务器是你自己掌控的,可以不用加密;如果用第三方存储,建议用 gpg 或者 rclone 的加密功能先加密再上传,防止数据泄露。
问:rsync 传输会不会很占带宽? 增量传输默认只传变化部分,日常备份的数据量很小。第一次全量同步会大一些,选择凌晨执行即可。
问:备份频率多久合适? 对个人博客,数据库每天备份一次、文件每周备份一次是底线。内容更新频繁的站可以加密频率,网站内容基本不变的可以放宽。
数据安全这件事,投入不大但回报极高。花半天时间把 rsync 异地备份搭起来,换来的是"服务器随便折腾、数据永远有底"的安心。希望读到这里的站长们,都能把备份这件事重视起来,别等到数据丢了再后悔。