数据库备份恢复策略全解:从理论到自动化实践
前言
"备份不做,删库跑路"——这虽然是玩笑话,但备份的重要性怎么强调都不过分。一套完善的备份恢复策略,是数据库运维的生命线。本文将涵盖MySQL、PostgreSQL、Redis的备份恢复方案,帮助企业建立可靠的数据库容灾体系。
一、备份策略基础
1.1 备份类型
| 维度 | 类型 | 说明 |
|---|---|---|
| 备份方式 | 物理备份 | 直接复制数据文件,速度快 |
| 逻辑备份 | 导出SQL语句,可读性好 | |
| 备份范围 | 全量备份 | 备份全部数据 |
| 增量备份 | 只备份变化的数据 | |
| 服务状态 | 热备份 | 不影响业务读写 |
| 温备份 | 可读不可写 | |
| 冷备份 | 需停止服务 |
1.2 RPO与RTO
RPO (Recovery Point Objective) - 恢复点目标
能容忍的最大数据丢失量
RTO (Recovery Time Objective) - 恢复时间目标
从故障到恢复的最大允许时间
示例:
核心交易系统: RPO=0, RTO<5分钟 → 需要实时同步+自动故障切换
报表系统: RPO=1小时, RTO<4小时 → 每小时备份即可
日志分析: RPO=1天, RTO<24小时 → 每天备份
1.3 备份策略矩阵
┌──────────────────────────────┐
│ 每日全量备份 │ ← crontab: 凌晨2点
└──────────┬───────────────────┘
│
┌──────────▼───────────────────┐
│ 每小时增量/差异备份 │ ← crontab: 每小时
└──────────┬───────────────────┘
│
┌──────────▼───────────────────┐
│ WAL/Redo 实时归档 │ ← archive_command
└──────────────────────────────┘
二、MySQL备份恢复
2.1 mysqldump 逻辑备份
#!/bin/bash
# mysql_backup.sh
BACKUP_DIR="/data/backup/mysql"
MYSQL_USER="backup"
MYSQL_PASS="password"
DB_LIST="mydb,appdb,logdb"
DATE=$(date +%Y%m%d_%H%M%S)
# 全量备份
mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASS" \
--single-transaction \ # 一致性快照
--routines \ # 存储过程和函数
--triggers \ # 触发器
--events \ # 事件调度
--databases $DB_LIST \
| gzip > "${BACKUP_DIR}/full_${DATE}.sql.gz"
# 只备份结构(不含数据)
mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASS" \
--no-data \
--databases $DB_LIST \
> "${BACKUP_DIR}/schema_${DATE}.sql"
2.2 XtraBackup 物理热备份
# 安装
yum install percona-xtrabackup-80 -y
# 全量备份
xtrabackup --backup \
--target-dir=/data/backup/mysql/full \
--user=backup --password=password \
--compress
# 增量备份(基于上次全量或增量)
xtrabackup --backup \
--target-dir=/data/backup/mysql/incr1 \
--incremental-basedir=/data/backup/mysql/full \
--user=backup --password=password \
--compress
# 准备备份(应用日志)
xtrabackup --prepare --apply-log-only \
--target-dir=/data/backup/mysql/full
xtrabackup --prepare \
--target-dir=/data/backup/mysql/full \
--incremental-dir=/data/backup/mysql/incr1
# 恢复
xtrabackup --copy-back --target-dir=/data/backup/mysql/full
chown -R mysql:mysql /var/lib/mysql
2.3 恢复验证
# 在测试环境验证备份
# 1. 启动临时MySQL
mysqld --datadir=/tmp/test_mysql --port=3307 --socket=/tmp/mysql_test.sock &
# 2. 恢复备份
mysql -S /tmp/mysql_test.sock < backup.sql
# 3. 校验数据
mysql -S /tmp/mysql_test.sock -e "
SELECT COUNT(*) FROM mydb.orders;
SELECT MAX(id) FROM mydb.users;
CHECKSUM TABLE mydb.orders;
"
三、PostgreSQL备份恢复
3.1 pg_dump 逻辑备份
# 单库备份
pg_dump -h localhost -U postgres \
-Fc \ # 自定义压缩格式
-f /data/backup/pg/mydb_${DATE}.dump \
mydb
# 全库备份
pg_dumpall -h localhost -U postgres \
--globals-only \ # 只备份全局对象(角色、表空间)
-f /data/backup/pg/globals_${DATE}.sql
3.2 pg_basebackup 物理备份
# 全量备份
pg_basebackup -h localhost -U repl_user \
-D /data/backup/pg/base_${DATE} \
-Fp -Xs -P -R
# 恢复
# 1. 停止PostgreSQL
systemctl stop postgresql
# 2. 清空数据目录
rm -rf /var/lib/pgsql/14/data/*
# 3. 复制备份
cp -r /data/backup/pg/base_${DATE}/* /var/lib/pgsql/14/data/
# 4. 恢复配置
# 删除 standby.signal (如果是恢复到新主库)
# 配置 postgresql.conf
# 5. 启动
systemctl start postgresql
3.3 PITR 时间点恢复
# 恢复到特定时间点
# recovery.conf / recovery.signal (PG12+)
restore_command = 'cp /data/pg_archive/%f %p'
recovery_target_time = '2025-06-12 10:30:00'
recovery_target_action = 'promote'
# 启动后自动恢复到指定时间点
四、Redis备份
4.1 RDB自动备份
#!/bin/bash
# redis_backup.sh
REDIS_DIR="/var/lib/redis"
BACKUP_DIR="/data/backup/redis"
# 触发BGSAVE
redis-cli BGSAVE
# 等待完成
TIMEOUT=300
ELAPSED=0
while [ $ELAPSED -lt $TIMEOUT ]; do
STATUS=$(redis-cli INFO persistence | grep rdb_bgsave_in_progress | cut -d: -f2)
if [ "$STATUS" -eq 0 ]; then
break
fi
sleep 1
ELAPSED=$((ELAPSED + 1))
done
# 检查并复制
if [ -f "${REDIS_DIR}/dump.rdb" ]; then
cp "${REDIS_DIR}/dump.rdb" "${BACKUP_DIR}/dump_$(date +%Y%m%d_%H%M).rdb"
echo "备份成功: $(du -h ${BACKUP_DIR}/dump_*.rdb | tail -1)"
fi
4.2 AOF备份
# 触发AOF重写(压缩AOF文件)
redis-cli BGREWRITEAOF
# 备份AOF
cp /var/lib/redis/appendonly.aof /data/backup/redis/aof_$(date +%Y%m%d_%H%M).aof
# 检查AOF是否损坏
redis-check-aof /var/lib/redis/appendonly.aof
# 修复
redis-check-aof --fix /var/lib/redis/appendonly.aof
五、自动化备份框架
5.1 统一备份脚本
#!/bin/bash
# unified_backup.sh
set -euo pipefail
# 配置
BACKUP_ROOT="/data/backup"
RETENTION_DAYS=30
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"
# 日志
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a /var/log/backup.log
}
# 通知
notify() {
local status="$1" msg="$2"
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\":\"[$status] 备份任务: $msg\"}" > /dev/null
}
# MySQL备份
backup_mysql() {
log "开始MySQL备份..."
mysqldump --single-transaction --all-databases \
| gzip > "${BACKUP_ROOT}/mysql/full_$(date +%Y%m%d).sql.gz"
local rc=$?
log "MySQL备份完成 (exit=$rc)"
return $rc
}
# 清理旧备份
cleanup() {
log "清理${RETENTION_DAYS}天前的备份..."
find "${BACKUP_ROOT}" -type f -mtime +"${RETENTION_DAYS}" -delete
}
# 主流程
main() {
log "===== 备份任务开始 ====="
local failed=0
backup_mysql || { failed=1; notify "FAIL" "MySQL备份失败"; }
backup_postgresql || { failed=1; notify "FAIL" "PG备份失败"; }
cleanup
if [ $failed -eq 0 ]; then
notify "OK" "所有备份任务完成"
fi
log "===== 备份任务结束 ====="
}
main "$@"
六、灾难恢复演练
6.1 演练流程
1. 准备阶段
- 确认备份文件可用
- 准备演练环境
2. 执行阶段
- 模拟故障(停止数据库)
- 执行恢复流程
- 计时记录
3. 验证阶段
- 数据完整性检查
- 业务功能测试
- 性能基准对比
4. 复盘阶段
- 记录耗时
- 发现问题
- 优化预案
6.2 恢复检查清单
□ 备份文件存在且大小>0
□ 备份文件MD5校验通过
□ 能在测试环境成功恢复
□ 数据行数与预期一致
□ 关键表CHECKSUM一致
□ 应用能正常连接
□ 业务查询正常返回
总结
备份恢复的黄金法则:
- 3-2-1原则:3份备份、2种介质、1份异地
- 自动化:脚本+定时任务,杜绝手工操作
- 验证:未验证的备份等于没有备份
- 文档:恢复步骤要清晰,紧急时刻不用想
- 演练:每季度至少一次灾难恢复演练