自动备份数据库到对象存储:mysqldump+cron+rclone 加密实战
2026-08-15 · DevCraft Studio
网站最值钱的是数据库。用 mysqldump 导出、cron 定时、rclone 同步到 B2/R2,再上加密与保留策略,睡得着觉。
站点最坏的事不是宕机,而是「以为有备份,真出事才发现备份是坏的,或者备份和数据库死在同一块硬盘上」。对一台 VPS 来说,最值钱、最难重建的就是数据库——文章、订单、用户数据全在里面。代码可以重新 git clone,数据库丢了就是真丢了。本文给你一条稳妥的自动备份链路:mysqldump(或 pg_dump)把库导出成 SQL,cron 定时跑,rclone 把文件同步到 Backblaze B2 或 Cloudflare R2 这类对象存储,最后再上加密和「保留 N 份」的滚动策略。照着做,你就能睡个安稳觉。
延伸阅读
更多相关攻略推荐:Ollama AI系列(2):VPS上的AI推理与API应用、Ollama AI系列(2):VPS上的AI推理与API应用、ARM / Ampere 席卷 VPS:性价比真香还是兼容陷阱?、【VPS 硬件选型指南 (内存 篇) 02】大内存 VPS 能干嘛?、2026 独立站 PCI-DSS 自查清单:SAQ A 还是 A-E。
一、为什么数据库备份最不能省
文件型备份(直接 tar 整个 /var/lib/mysql 目录)对运行中的数据库是危险的:拷贝过程中库在写,得到的文件大概率是损坏、不一致的。数据库需要自己的导出工具来保证「某一时刻的一致性快照」,这就是 mysqldump(MySQL / MariaDB)和 pg_dump(PostgreSQL)存在的意义。
另一条铁律是 3-2-1 的变体:备份不能和数据库待在同一块磁盘。本地 dump 能挡住手滑 DROP TABLE,但挡不住整盘损坏、机房故障、或被勒索软件一锅端。所以真正可靠的方案是「本地导出 + 异地对象存储」,本文后半段用 rclone 解决的就是这第二步。
还要提醒一句:没演练过的备份等于没备份。至少每季度挑一份 dump 恢复到临时库,确认能正常导入、数据条数对得上。很多人的「备份」最后发现是空文件或半个事务,就是从来没恢复过。
那到底该备多频繁、留多久?没有一刀切答案,但有个简单公式:备份频率不低于你「能承受丢失的数据量」。博客一天更一篇,每天备一次、丢一天能接受;电商订单每秒都在产生,就得做 binlog 增量备份甚至主从复制,把 RPO(恢复点目标)压到分钟级。留存天数则取决于磁盘和库大小——200MB 的库留 14 天只是几百 MB,5GB 的库留 90 天就是 450GB,得先算清楚再设,别让「保险」反而把盘撑爆。下面这套方案默认日备加 7 天滚动,你可以照自己的 RPO/RTO 往上加。
二、mysqldump / pg_dump:一行导出 + 一致性
MySQL / MariaDB 最常用的一行是:
mysqldump -u root --single-transaction --routines --triggers --events \
--all-databases | gzip > /backups/mysql/full_$(date +%F).sql.gz逐条解释:--single-transaction 对 InnoDB 表开启一个一致性读快照,不锁表,网站在备份时照常读写,这是线上库必须用的一档;--routines --triggers --events 把存储过程、触发器和事件也一并导出,避免只备了表漏了逻辑;--all-databases 一次性导出全部库。如果是 MyISAM 表(老项目常见),--single-transaction 不起作用,得退回 --lock-tables,所以先 SHOW TABLE STATUS 确认存储引擎。
PostgreSQL 对应的是:
pg_dump -U postgres -Fc mydb > /backups/pg/mydb_$(date +%F).dump
pg_dumpall -U postgres > /backups/pg/all_$(date +%F).sql-Fc 产出自定义压缩格式,支持并行恢复,比纯文本更省空间也更灵活;pg_dumpall 则连角色和权限一起导。恢复时用 pg_restore -U postgres -d mydb -C 文件.dump。
密码别写在命令行。把凭据放进 ~/.my.cnf 的 [client] 段(user / password),再 chmod 600 ~/.my.cnf,这样 mysqldump 不用在命令里带 -p,密码也不会出现在进程列表(ps)里被人看到。
三、cron 定时与失败告警
把上面的命令包成一个脚本 /usr/local/bin/mysql_backup.sh,关键三件事:压缩、滚动清理、失败可见。脚本骨架:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
mysqldump --defaults-extra-file=/etc/mysql/backup.cnf \
--single-transaction --routines --triggers --events \
--all-databases | gzip > "$BACKUP_DIR/full_$DATE.sql.gz"
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup done: $BACKUP_DIR/full_$DATE.sql.gz"set -euo pipefail 保证任一步出错脚本就以非零状态退出;配合 find ... -mtime +7 -delete 只留最近 7 天,磁盘不会被备份撑爆。注意管道里 mysqldump 失败常被 gzip 吞掉退出码,严谨点要查 ${PIPESTATUS[0]} 再决定是否报警。
加进 crontab,每天凌晨 2 点跑(避开整点高峰,错开几分钟更好):
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1失败告警是关键一环。静默失败的定时任务比没备份更糟,因为你以为自己有保护。做法有几种:给脚本配一个「心跳」——成功后 curl 一下监控服务的 URL(如 Healthchecks / CronJobPro),没收到心跳就发邮件或 Slack;或者在脚本出错时发邮件。最小可用版是在脚本开头放 trap,异常退出时 curl 那个服务的 /fail 端点。
四、用 rclone 把备份送上对象存储(B2 / R2)
本地 dump 只完成了一半,下一步是把文件推到异地。rclone 是干这个的瑞士军刀,支持 S3、B2、R2 等几十种后端,自带断点续传、并发和校验,比自己拼 aws cli 省心。
Backblaze B2 是性价比标杆,S3 兼容,价格约 6 美元/TB/月,上传永久免费;Cloudflare R2 则主打出口流量免费,适合你要经常回拉备份的场景。先交互式配置一个 remote:
rclone config
# 选 n 新建,类型选 S3(R2)或 B2(Backblaze)
# R2 端点形如 https://<账户ID>.r2.cloudflarestorage.com然后用 sync 把本地备份目录推上去。--fast-list 让 rclone 一次列出整桶、少打 API 调用;--transfers 控制并发;--bwlimit 限速避免打满上行;断点续传和重试 rclone 默认就会做:
rclone sync /backups/mysql b2:my-backup-bucket/mysql \
--fast-list --transfers 8 --bwlimit 50M --progress把这条命令接在备份脚本末尾,或单独再建一个 cron,就能实现「本地导出 + 异地留存」。验证一下是否真的传上去了:rclone ls b2:my-backup-bucket/mysql。B2 还建议给桶开 Default encryption(SSE-B2)做静态加密,多一层兜底。
还有一个容易忽略的点:rclone 的上行流量会计入你 VPS 的月流量包。Contabo、RackNerd 这类厂商通常给得很宽裕(有的每月数 TB),日常几 GB 的数据库备份远碰不到上限;但如果你库很大、又开了高频备份,还是该用 --bwlimit 限速,既避免打满上行影响线上用户,也防止某天突发同步把整月额度用光被限速甚至额外计费。Vultr 的流量是另算的(超出套餐后按量计费),更要注意。另一个好习惯是给备份桶单独建一个最小权限的 Application Key / API Token,只给这一个桶的读写,万一密钥泄露,影响面也被锁在这一个桶里,不至于波及其他数据。
五、加密与「保留 N 份」策略
备份落到别人家的存储桶,里面是你的全部业务数据,加密必须自己掌握密钥,不能只依赖厂商的服务器端加密。两条路子:
其一是文件级加密,导出后用 age(现代、简单、基于 X25519)或 gpg 先加密再上传:
# age:生成密钥对
age-keygen -o key.txt
# 加密 dump
age --recipient $(grep -oP 'public key: \K.*' key.txt) full.sql.gz > full.sql.gz.age
# 解密
age --decrypt -i key.txt full.sql.gz.age > full.sql.gz其二是 rclone 自带的 crypt 后端:新建一个 type=crypt 的 remote 包住对象存储桶,文件名和目录名都能加密,之后直接 rclone sync /backups b2-encrypted:mysql 即可,rclone 在传输前就加密好。密钥(age 的私钥、crypt 的 password)务必离线保存,丢了就再也解不开。
「保留 N 份」就是滚动留存,避免只有一份、一旦当天的备份正好损坏就全完。常见组合:每日留 7 份、每周留 4 份、每月留 12 份。用 find 做天数滚动最简单;追求更精细的「每日/每周/每月」分级,可让 rclone 配合对象存储的生命周期规则(B2 的 Lifecycle Rules 删 30 天前的旧版本),但注意:如果你用的是 restic / borg 这类自己管去重的仓库,不要对仓库桶开 lifecycle,否则可能删掉 pack 文件把仓库弄坏——只有对原始文件备份(rclone sync)开生命周期才安全。
延伸阅读:备份属于「数据安全」的一层,建议连同 VPS 安全基线 一起做;想了解更省事的快照方案,看 VPS 快照与备份 2026;挑机器可参考 VPS 新手选购指南。