数据库定期备份脚本编写
为什么备份脚本比备份本身更重要
很多站点把备份当成一次性的任务:装好数据库,手动导出一份数据,放在服务器某个目录里,就觉得高枕无忧了。直到某天硬盘故障、误删表、或者被入侵后数据被加密,才想起那份备份——结果发现是三个月前的,恢复出来的东西几乎没法用。
定期备份的意义在于“最近可恢复点”。而脚本的作用,就是把这个恢复点尽量往前推,并且不需要你每次都记得去执行。
先想清楚要备份什么
写脚本之前,先明确几件事:
- 01数据库类型
MySQL、PostgreSQL、SQLite 的备份方式完全不同。
- 02备份粒度
全量备份最简单,但数据量大时耗时长、占空间;增量备份节省资源,但恢复时依赖完整链,出问题更隐
- 03保留策略
保留最近7天每日备份、最近4周每周备份、最近6个月每月备份,是比较常见的做法。保留太久会占满
- 04存储位置
最好和数据库所在服务器分离。同一台机器上,硬盘挂了备份也跟着挂。异地、对象存储、另一台内网机
一个可用的 MySQL 备份脚本示例
以下脚本适合中小型站点,逻辑清晰,也容易改成其他数据库。
“`bash
#!/bin/bash
# 基础配置
DB_USER="backup_user"
DB_PASS="YourStrongPassword"
DB_NAME="your_db"
BACKUP_DIR="/data/backups/mysql"
DATE=$(date +%F)
RETENTION_DAYS=7
# 创建当日目录
mkdir -p "$BACKUP_DIR/$DATE"
# 执行全量备份,同时压缩
mysqldump -u"$DB_USER" -p"$DB_PASS" –single-transaction –routines –triggers "$DB_NAME" | gzip > "$BACKUP_DIR/$DATE/$DB_NAME.sql.gz"
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') 备份成功:$DB_NAME.sql.gz"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') 备份失败,请检查错误日志"
# 可以在这里追加发送告警邮件的命令
exit 1
fi
# 清理7天前的备份
find "$BACKUP_DIR" -type d -name "20*" -mtime +$RETENTION_DAYS -exec rm -rf {} ;
“`
几个细节值得说明:
- `–single-transaction` 对 InnoDB 表有效,备份过程中不影响业务读写,避免锁表。
- `–routines` 和 `–triggers` 把存储过程、触发器也带上,否则恢复后可能缺东西。
- `find` 是按目录的修改时间清理的,确保只删掉过期的整份备份。
如果你用的是 PostgreSQL,把 `mysqldump` 换成 `pg_dump`,再加上 `pg_restore` 验证即可。SQLite 更简单,直接复制数据库文件,但要注意在复制前执行 `VACUUM` 或使用备份 API,避免文件不一致。
让脚本真正跑起来
写好的脚本不会自动执行,需要配合系统定时任务。
把脚本放到 `/usr/local/bin/db_backup.sh` 并加上执行权限:
“`bash
chmod +x /usr/local/bin/db_backup.sh
“`
然后编辑 crontab:
“`bash
crontab -e
“`
加入:
“`cron
20 2 * * * /usr/local/bin/db_backup.sh >> /var/log/db_backup.log 2>&1
“`
表示每天凌晨 2:20 执行一次。选择凌晨是因为业务低峰,备份对数据库性能的影响最小。日志输出到独立文件里,方便事后排查。
这里有个容易踩的坑:`mysqldump` 和 `find` 等命令的路径,在 cron 环境下可能与登录 shell 不同。脚本第一行最好加上绝对路径,或者手动指定环境变量。在实战中,很多“备份没执行成功”的根源,不是脚本写错了,而是 cron 里 PATH 不对。
备份之后,还得验证
备份脚本能跑,不代表备份就有用。常见的失败原因包括:
- 磁盘空间不足,gzip 写了一半,生成一个损坏的文件。
- 密码变更后,脚本里的旧密码失效。
- 数据库版本升级,老版本的 `mysqldump` 不兼容新版本的数据字典。
所以务必在脚本中检查上一步的返回值,并且至少每周手动做一次恢复测试。恢复测试不用真的覆盖生产库,可以在本地起一个新实例,导入备份文件,跑几条 count 查询,确认表数量和行数合理就行。
有条件的话,给备份文件做一次校验和记录。例如:
“`bash
md5sum "$BACKUP_DIR/$DATE/$DB_NAME.sql.gz" >> "$BACKUP_DIR/$DATE/checksum.txt"
“`
将来需要恢复时,先核对 md5,就能判断文件是否完整。
打开告警通道
备份是运维里最不适合“闷头干活”的事。脚本执行失败,最好第一时间通过邮件、短信或企业微信通知到人。简单做法是在脚本的错误分支里调 curl 请求告警接口,或者用系统自带的 `mail` 命令。具体用什么渠道不重要,重要的是“失败一定能被发现”。
如果公司已有监控系统,也可以让备份脚本输出 Prometheus 格式的指标,比如备份耗时、备份文件大小、是否成功,再配一个告警规则。这样比每天翻日志要可靠得多。
数据库备份脚本的编写,本质上不是技术难题,而是习惯问题。把备份周期定好,脚本写清楚,告警接上,再定期做一次恢复演练,数据安全就算有了基本保障。等到哪天真出了事,你会感谢那个把脚本写在凌晨两点二十的自己。