数据库与文件备份策略
如果你没有备份,你就没有生产环境,只是在赌运气。
备份对象与方式
| 对象 | 备份方式 | 工具 | 频率建议 |
|---|---|---|---|
| 关系型数据库 | 逻辑导出(可跨版本恢复) | pg_dump / mysqldump | 每日 |
| 数据库(大型) | 物理快照(快但需相同版本) | pg_basebackup / 云快照 | 每日 |
| 用户上传文件 | 增量同步到对象存储 | rclone / rsync | 每小时 |
| 配置文件 | Git 或加密文件备份 | git / gpg | 每次变更 |
备份流程图
graph LR
A[数据库 pg_dump/mysqldump] --> B[本地 /backup/]
B --> C[压缩 + 加密]
C --> D[上传对象存储 S3/GCS/OSS]
E[上传目录 /data/uploads] --> F[rclone 增量同步]
F --> D
G[配置文件 /etc/myapp] --> H[Git 仓库/加密备份]
PostgreSQL 备份与恢复
# === 备份 ===
# 压缩格式(推荐,支持并行恢复)
pg_dump -U postgres -Fc mydb -f /backup/mydb-$(date +%F).dump
# SQL 文本格式(可阅读,便于部分恢复)
pg_dump -U postgres mydb | gzip > /backup/mydb-$(date +%F).sql.gz
# === 恢复 ===
# 从压缩格式恢复(-j 4 并行,加速大库)
pg_restore -U postgres -d mydb -j 4 /backup/mydb-2026-03-22.dump
# 从 SQL 文本格式恢复
gunzip < /backup/mydb-2026-03-22.sql.gz | psql -U postgres mydb
# 验证备份完整性(不执行恢复,只列出内容)
pg_restore --list /backup/mydb-2026-03-22.dump | head -20
MySQL / MariaDB 备份与恢复
# === 备份 ===
# --single-transaction 保证 InnoDB 一致性快照(不锁表)
mysqldump -u root -p --single-transaction --routines --triggers mydb \
| gzip > /backup/mydb-$(date +%F).sql.gz
# 所有库(生产常用)
mysqldump -u root -p --all-databases --single-transaction \
| gzip > /backup/all-dbs-$(date +%F).sql.gz
# === 恢复 ===
gunzip < /backup/mydb-2026-03-22.sql.gz | mysql -u root -p mydb
完整生产备份脚本
#!/usr/bin/env bash
# backup.sh — PostgreSQL + 文件备份,加密上传至对象存储
set -euo pipefail
BACKUP_DIR=/backup
DATE=$(date +%F)
DB_NAME=mydb
DB_USER=postgres
REMOTE=s3:my-prod-backups
ENCRYPT_KEY=/etc/backup/backup.key # openssl 加密密钥文件
LOG=/var/log/backup.log
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
# 1. 数据库备份
log "开始数据库备份"
pg_dump -U "$DB_USER" -Fc "$DB_NAME" -f "$BACKUP_DIR/db-$DATE.dump"
# 2. 加密(可选,对合规要求高的场景)
openssl enc -aes-256-cbc -salt -pbkdf2 \
-in "$BACKUP_DIR/db-$DATE.dump" \
-out "$BACKUP_DIR/db-$DATE.dump.enc" \
-pass file:"$ENCRYPT_KEY"
rm "$BACKUP_DIR/db-$DATE.dump" # 删除明文
# 3. 上传文件备份
log "同步上传目录"
rclone copy /data/uploads "$REMOTE/uploads-$DATE/" --progress
# 4. 上传加密 dump
log "上传数据库备份"
rclone copy "$BACKUP_DIR/db-$DATE.dump.enc" "$REMOTE/db/"
# 5. 清理本地备份(保留最近 7 天)
find "$BACKUP_DIR" -name "*.dump.enc" -mtime +7 -delete
log "备份完成: db-$DATE.dump.enc"
备份保留策略(3-2-1 原则)
| 保留周期 | 份数 | 说明 |
|---|---|---|
| 每日备份 | 保留 7 份 | 覆盖过去 7 天,用于快速恢复 |
| 每周备份 | 保留 4 份 | 每周日保留,覆盖过去 4 周 |
| 每月备份 | 保留 12 份 | 每月 1 日保留,覆盖过去 1 年 |
3 份副本,2 种介质,1 份异地:本地磁盘 + 对象存储 + 跨区域复制。
自动清理旧备份的 cron 示例:
# 每日 2:00 执行备份,每日 3:00 清理超过 7 天的本地备份
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
0 3 * * * find /backup -name "*.dump.enc" -mtime +7 -delete
逻辑备份 vs 物理备份
| 对比 | 逻辑备份(pg_dump) | 物理备份(pg_basebackup/快照) |
|---|---|---|
| 跨版本恢复 | ✅ 可以 | ❌ 需相同主版本 |
| 恢复速度 | 慢(重放 SQL) | 快(文件复制) |
| 文件大小 | 较小(可压缩) | 较大 |
| 适合大库 | 小于 10GB 推荐 | 大库(>10GB)推荐 |
| 部分恢复 | ✅ 可以(指定表) | ❌ 全库恢复 |
常见误区
| 误区 | 正确做法 |
|---|---|
| 备份只留在同一台机器上 | 必须异地(对象存储 / 跨机房) |
| 只备份数据库,不备份上传文件 | 用户上传的图片、附件同样是生产数据 |
| 有备份但从没做过恢复测试 | 每季度至少做一次恢复演练(见下一节) |
mysqldump 不加 --single-transaction | 不加会锁表,生产环境不可接受 |
| 备份文件明文存储 | 涉及用户隐私/支付的数据,备份文件必须加密 |
本节执行清单
- [ ] 确认 PostgreSQL 或 MySQL 备份命令可正常执行
- [ ] 把备份脚本加入 cron(每日自动运行)
- [ ] 把备份副本上传至异地对象存储
- [ ] 设置备份保留策略(日/周/月)
- [ ] 记录最近一次成功备份时间
下一节:恢复演练与灾备意识——备份的价值只有在恢复时才真正体现。