返回 筑基・数据元府藏真
备份恢复与灾难恢复策略
博主
大约 12 分钟
备份恢复与灾难恢复策略
一、问题引入:误删数据的恐慌
1.1 真实案例:生产数据误删除事故
事故时间:周四下午3点
事故场景:新入职开发人员在生产环境执行测试脚本
事故经过:
┌─────────────────────────────────────────────────────────────┐
│ T1 15:00 - 开发人员连接生产数据库 │
│ 原意:在测试环境清理测试数据 │
│ 实际:连接字符串配置错误,连接到了生产库 │
│ │
│ T2 15:05 - 执行清理脚本 │
│ DELETE FROM orders WHERE created_at < '2024-01-01' │
│ 影响:删除了50万条历史订单数据 │
│ │
│ T3 15:10 - 用户反馈订单查询异常 │
│ 客服接到多个用户投诉无法查看历史订单 │
│ │
│ T4 15:15 - 发现问题,停止删除操作 │
│ 已删除数据:50万条订单记录 │
│ 涉及用户:约10万人 │
│ │
│ T5 15:20 - 开始评估恢复方案 │
│ 方案1:从备份恢复 → 上次全量备份是昨天凌晨2点 │
│ 意味着丢失13小时的数据 │
│ 方案2:使用Binlog闪回 → 需要解析大量Binlog │
│ │
│ T6 15:30 - 启动恢复流程 │
│ 1. 找到昨天全量备份 │
│ 2. 恢复全量备份到临时实例 │
│ 3. 解析Binlog,提取15:00后的变更 │
│ 4. 合并数据,验证一致性 │
│ │
│ T7 18:00 - 数据恢复完成 │
│ 恢复时间:2小时45分钟 │
│ 业务影响:订单服务不可用3小时 │
│ 直接损失:订单损失约200万,赔付用户100万 │
└─────────────────────────────────────────────────────────────┘
事故反思:
1. 权限管理不当:开发人员有生产库DELETE权限
2. 备份策略不足:只有每日全量备份,没有实时Binlog备份
3. 恢复流程不熟悉:恢复耗时过长
4. 缺乏防误删机制:没有延迟从库、回收站等保护
改进措施:
1. 实施最小权限原则
2. 建立3-2-1备份策略
3. 部署延迟从库(1小时延迟)
4. 定期进行恢复演练
5. 实施操作审计和审批流程
1.2 数据丢失风险分析
数据丢失的常见原因:
┌──────────────────────────────────────────────────────────────┐
│ │
│ 人为操作(40%) │
│ - 误删除(DELETE/DROP/TRUNCATE) │
│ - 错误更新(UPDATE无WHERE条件) │
│ - 错误的DDL操作 │
│ │
│ 硬件故障(25%) │
│ - 磁盘损坏 │
│ - 服务器宕机 │
│ - 存储阵列故障 │
│ │
│ 软件故障(20%) │
│ - 数据库Bug │
│ - 操作系统崩溃 │
│ - 应用Bug导致数据损坏 │
│ │
│ 灾难事件(10%) │
│ - 机房火灾/水灾 │
│ - 地震等自然灾害 │
│ - 电力故障 │
│ │
│ 安全事件(5%) │
│ - 勒索软件加密 │
│ - 恶意删除 │
│ - 数据泄露后删除 │
│ │
└──────────────────────────────────────────────────────────────┘
二、备份策略设计
2.1 3-2-1备份原则
3-2-1备份原则:
┌──────────────────────────────────────────────────────────────┐
│ │
│ 3 - 至少3份数据副本 │
│ - 1份生产数据 │
│ - 1份本地备份 │
│ - 1份异地备份 │
│ │
│ 2 - 使用2种不同存储介质 │
│ - 例如:磁盘 + 磁带/云存储 │
│ - 防止单种介质故障 │
│ │
│ 1 - 至少1份异地备份 │
│ - 防止本地灾难(火灾、地震) │
│ - 建议距离>100公里 │
│ │
└──────────────────────────────────────────────────────────────┘
备份类型选择:
┌──────────────┬────────────────────────────────────────────────┐
│ 备份类型 │ 特点 │
├──────────────┼────────────────────────────────────────────────┤
│ 全量备份 │ 完整数据,恢复简单,占用空间大,耗时长 │
│ 增量备份 │ 只备份变化数据,节省空间,恢复复杂 │
│ 差异备份 │ 备份自上次全量后的变化,平衡方案 │
│ Binlog备份 │ 记录所有变更,支持时间点恢复 │
└──────────────┴────────────────────────────────────────────────┘
2.2 备份策略矩阵
| 数据重要性 | 备份频率 | 保留周期 | RTO目标 | RPO目标 | 推荐方案 |
|---|---|---|---|---|---|
| 核心数据 | 每日全量+实时Binlog | 30天 | 1小时 | 5分钟 | XtraBackup + Binlog |
| 重要数据 | 每日全量+每小时增量 | 14天 | 4小时 | 1小时 | mysqldump + 增量 |
| 一般数据 | 每周全量+每日增量 | 7天 | 8小时 | 24小时 | 逻辑备份 |
| 归档数据 | 每月全量 | 1年 | 24小时 | 1月 | 冷备份 |
2.3 备份工具对比
| 工具 | 类型 | 速度 | 恢复速度 | 适用场景 |
|---|---|---|---|---|
| mysqldump | 逻辑 | 慢 | 慢 | 小数据量,跨版本恢复 |
| XtraBackup | 物理 | 快 | 快 | 大数据量,热备份 |
| mydumper | 逻辑 | 中等 | 中等 | 多线程逻辑备份 |
| Clone Plugin | 物理 | 快 | 快 | MySQL 8.0+,实例克隆 |
三、备份实施详解
3.1 mysqldump逻辑备份
#!/bin/bash
# 全量备份脚本
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="mydb"
RETENTION_DAYS=30
# 创建备份目录
mkdir -p ${BACKUP_DIR}/${DATE}
# 执行全量备份
mysqldump \
--host=localhost \
--user=backup \
--password='backup_password' \
--single-transaction \
--routines \
--triggers \
--events \
--master-data=2 \
--all-databases \
| gzip > ${BACKUP_DIR}/${DATE}/full_backup.sql.gz
# 备份配置文件和日志
cp /etc/my.cnf ${BACKUP_DIR}/${DATE}/
cp -r /var/log/mysql ${BACKUP_DIR}/${DATE}/
# 计算校验和
cd ${BACKUP_DIR}/${DATE}
md5sum full_backup.sql.gz > checksum.md5
# 清理旧备份
find ${BACKUP_DIR} -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \;
# 上传至异地存储
aws s3 sync ${BACKUP_DIR}/${DATE} s3://my-backup-bucket/mysql/${DATE}/
# 发送通知
echo "Backup completed: ${DATE}" | mail -s "MySQL Backup" admin@company.com
# 单表备份
mysqldump --single-transaction mydb orders > orders_backup.sql
# 仅备份结构
mysqldump --no-data mydb > schema_backup.sql
# 仅备份数据
mysqldump --no-create-info mydb > data_backup.sql
# 特定条件备份
mysqldump --where="created_at > '2024-01-01'" mydb orders > recent_orders.sql
# 并行备份(mydumper)
mydumper -u backup -p password -B mydb -o /backup/mydumper/ \
--threads=4 \
--compress \
--triggers \
--routines \
--events
3.2 XtraBackup物理备份
#!/bin/bash
# XtraBackup全量备份脚本
BACKUP_DIR="/backup/xtrabackup"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="${BACKUP_DIR}/full/${DATE}"
# 全量备份
xtrabackup \
--backup \
--target-dir=${FULL_BACKUP_DIR} \
--user=backup \
--password='backup_password' \
--parallel=4 \
--compress \
--compress-threads=4
# 记录Binlog位置
cat ${FULL_BACKUP_DIR}/xtrabackup_binlog_info
# 准备备份(应用日志)
xtrabackup \
--prepare \
--target-dir=${FULL_BACKUP_DIR}
# 压缩备份
tar czvf ${FULL_BACKUP_DIR}.tar.gz -C ${FULL_BACKUP_DIR} .
# 上传至云存储
aws s3 cp ${FULL_BACKUP_DIR}.tar.gz s3://my-backup-bucket/xtrabackup/
# 增量备份
#!/bin/bash
BASE_DIR="/backup/xtrabackup/full/latest"
INCR_DIR="/backup/xtrabackup/incr/$(date +%Y%m%d_%H%M%S)"
# 基于全量备份的增量备份
xtrabackup \
--backup \
--target-dir=${INCR_DIR} \
--incremental-basedir=${BASE_DIR} \
--user=backup \
--password='backup_password'
# 基于上次增量的增量备份
LAST_INCR=$(ls -td /backup/xtrabackup/incr/*/ | head -1)
xtrabackup \
--backup \
--target-dir=${INCR_DIR} \
--incremental-basedir=${LAST_INCR} \
--user=backup \
--password='backup_password'
3.3 Binlog实时备份
#!/bin/bash
# Binlog实时备份脚本
BACKUP_DIR="/backup/binlog"
MYSQL_USER="backup"
MYSQL_PASS="backup_password"
# 获取当前Binlog文件和位置
CURRENT_LOG=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -e "SHOW MASTER STATUS\G" | grep File | awk '{print $2}')
CURRENT_POS=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -e "SHOW MASTER STATUS\G" | grep Position | awk '{print $2}')
echo "Current binlog: ${CURRENT_LOG} at position ${CURRENT_POS}"
# 使用mysqlbinlog实时备份
mysqlbinlog \
--read-from-remote-server \
--host=localhost \
--user=${MYSQL_USER} \
--password=${MYSQL_PASS} \
--raw \
--stop-never \
--stop-never-slave-server-id=2000 \
${CURRENT_LOG} > ${BACKUP_DIR}/${CURRENT_LOG} &
# 或使用伪从库方式备份
cat > /etc/my.cnf.d/binlog-backup.cnf <<EOF
[mysqld]
server-id=2000
log_bin=/backup/binlog/mysql-bin
EOF
# 启动伪从库
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -e "
CHANGE MASTER TO
MASTER_HOST='master_host',
MASTER_USER='repl',
MASTER_PASSWORD='repl_password',
MASTER_LOG_FILE='${CURRENT_LOG}',
MASTER_LOG_POS=${CURRENT_POS};
START SLAVE SQL_THREAD;
"
四、恢复流程详解
4.1 全量恢复流程
#!/bin/bash
# XtraBackup全量恢复脚本
BACKUP_DIR="/backup/xtrabackup/full/20240115_020000"
DATA_DIR="/var/lib/mysql"
# 1. 停止MySQL
systemctl stop mysqld
# 2. 备份当前数据(可选)
mv ${DATA_DIR} ${DATA_DIR}.corrupt.$(date +%Y%m%d_%H%M%S)
mkdir -p ${DATA_DIR}
# 3. 准备备份(如果之前没准备过)
xtrabackup --prepare --target-dir=${BACKUP_DIR}
# 4. 恢复数据
xtrabackup \
--copy-back \
--target-dir=${BACKUP_DIR} \
--datadir=${DATA_DIR}
# 5. 修改权限
chown -R mysql:mysql ${DATA_DIR}
chmod 750 ${DATA_DIR}
# 6. 启动MySQL
systemctl start mysqld
# 7. 验证数据
mysql -e "SELECT COUNT(*) FROM mydb.orders;"
4.2 时间点恢复(PITR)
#!/bin/bash
# 时间点恢复脚本
BACKUP_DIR="/backup/xtrabackup/full/latest"
BINLOG_DIR="/backup/binlog"
TARGET_TIME="2024-01-15 15:00:00"
DATA_DIR="/var/lib/mysql"
# 1. 恢复全量备份
systemctl stop mysqld
rm -rf ${DATA_DIR}
xtrabackup --copy-back --target-dir=${BACKUP_DIR} --datadir=${DATA_DIR}
chown -R mysql:mysql ${DATA_DIR}
systemctl start mysqld
# 2. 获取备份时的Binlog位置
BINLOG_FILE=$(cat ${BACKUP_DIR}/xtrabackup_binlog_info | awk '{print $1}')
BINLOG_POS=$(cat ${BACKUP_DIR}/xtrabackup_binlog_info | awk '{print $2}')
echo "Starting from binlog: ${BINLOG_FILE} at position ${BINLOG_POS}"
# 3. 应用Binlog到指定时间点
mysqlbinlog \
--start-position=${BINLOG_POS} \
--stop-datetime="${TARGET_TIME}" \
${BINLOG_DIR}/${BINLOG_FILE} \
${BINLOG_DIR}/mysql-bin.$((${BINLOG_FILE##*.} + 1)) \
| mysql -u root -p
echo "Recovery completed to ${TARGET_TIME}"
4.3 误删除恢复(Binlog闪回)
# 使用binlog2sql进行闪回
# 安装:pip install binlog2sql
# 1. 解析Binlog生成回滚SQL
python binlog2sql.py \
-h localhost \
-u backup \
-p 'backup_password' \
-d mydb \
-t orders \
--start-file='mysql-bin.000001' \
--start-datetime='2024-01-15 15:00:00' \
--stop-datetime='2024-01-15 15:05:00' \
-B > rollback.sql
# 2. 预览回滚SQL
cat rollback.sql
# 3. 执行回滚
mysql -u root -p mydb < rollback.sql
# 或使用MyFlash工具
# 安装MyFlash
wget https://github.com/Meituan-Dianping/MyFlash/archive/master.zip
# 生成回滚SQL
./flashback --binlogFileNames=/var/lib/mysql/mysql-bin.000001 \
--start-datetime="2024-01-15 15:00:00" \
--stop-datetime="2024-01-15 15:05:00" \
--databaseNames=mydb \
--tableNames=orders
# 应用回滚
mysqlbinlog binlog_output_base.flashback | mysql -u root -p
五、灾难恢复方案
5.1 RTO/RPO目标设定
RTO(恢复时间目标)vs RPO(恢复点目标):
┌──────────────────────────────────────────────────────────────┐
│ │
│ 时间轴: │
│ │
│ 故障发生 │
│ │ │
│ │<------- RPO ------->│ │
│ │ │ │
│ 最后备份 故障点 │
│ │ │ │
│ │ │<--------- RTO --------->│ │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ 服务恢复 │
│ │ │ │
│ │<---------------- 数据丢失 ------------------->│ │
│ │
│ RPO:可接受的数据丢失量(时间) │
│ RTO:可接受的恢复时间 │
│ │
│ 业务分级: │
│ ┌────────────┬─────────┬─────────┐ │
│ │ 业务级别 │ RTO │ RPO │ │
│ ├────────────┼─────────┼─────────┤ │
│ │ 核心系统 │ 1小时 │ 5分钟 │ │
│ │ 重要系统 │ 4小时 │ 1小时 │ │
│ │ 一般系统 │ 24小时 │ 24小时 │ │
│ └────────────┴─────────┴─────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
5.2 灾难恢复预案
# 数据库灾难恢复预案
## 1. 灾难分级
### P0 - 核心系统完全不可用
- 场景:主库宕机、数据中心故障
- 响应时间:5分钟内
- RTO:1小时
- RPO:5分钟
### P1 - 核心系统部分不可用
- 场景:主库性能严重下降、网络分区
- 响应时间:15分钟内
- RTO:2小时
- RPO:15分钟
### P2 - 非核心系统故障
- 场景:从库故障、备份失败
- 响应时间:1小时内
- RTO:4小时
- RPO:1小时
## 2. 恢复流程
### 主库故障恢复流程
1. **故障确认**(5分钟)
- 确认主库确实不可用
- 评估影响范围
- 通知相关团队
2. **切换决策**(5分钟)
- 尝试重启主库
- 如无法恢复,启动切换流程
3. **主从切换**(10分钟)
- 选择延迟最小的从库
- 停止从库复制
- 将从库提升为主库
- 更新应用连接配置
4. **数据恢复**(30分钟)
- 如需要,从备份恢复数据
- 应用Binlog到目标时间点
5. **验证恢复**(10分钟)
- 验证数据完整性
- 验证应用连接
- 恢复业务流量
### 数据误删除恢复流程
1. **停止写入**(立即)
- 停止相关应用写入
- 防止数据覆盖
2. **评估影响**(10分钟)
- 确认删除的数据范围
- 确认删除时间点
3. **选择恢复方案**
- 方案A:延迟从库恢复(如果有)
- 方案B:Binlog闪回
- 方案C:备份恢复
4. **执行恢复**
- 根据方案执行具体恢复操作
5. **数据验证**
- 对比删除前后的数据量
- 抽样验证数据正确性
## 3. 联系人清单
| 角色 | 姓名 | 电话 | 邮箱 |
|-----|------|------|------|
| DBA负责人 | 张三 | 13800138000 | zhangsan@company.com |
| 运维负责人 | 李四 | 13800138001 | lisi@company.com |
| 开发负责人 | 王五 | 13800138002 | wangwu@company.com |
| 业务负责人 | 赵六 | 13800138003 | zhaoliu@company.com |
## 4. 资源清单
### 备用服务器
- 服务器A:192.168.1.100(8核32G)
- 服务器B:192.168.1.101(8核32G)
### 存储资源
- 本地备份:/backup(10TB)
- 异地备份:s3://backup-bucket
### 工具
- XtraBackup:/usr/bin/xtrabackup
- mydumper:/usr/bin/mydumper
- binlog2sql:/opt/binlog2sql
5.3 自动化恢复脚本
#!/bin/bash
# 自动化故障切换脚本
MASTER_HOST="192.168.1.10"
SLAVE_HOSTS=("192.168.1.11" "192.168.1.12" "192.168.1.13")
VIP="192.168.1.100"
# 1. 检查主库状态
echo "Checking master status..."
mysql -h${MASTER_HOST} -e "SELECT 1" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Master is alive, no failover needed"
exit 0
fi
echo "Master is down, starting failover..."
# 2. 找到延迟最小的从库
MIN_DELAY=999999
NEW_MASTER=""
for slave in ${SLAVE_HOSTS[@]}; do
delay=$(mysql -h${slave} -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master | awk '{print $2}')
if [ "$delay" != "NULL" ] && [ $delay -lt $MIN_DELAY ]; then
MIN_DELAY=$delay
NEW_MASTER=$slave
fi
done
if [ -z "$NEW_MASTER" ]; then
echo "No available slave found!"
exit 1
fi
echo "Selected new master: ${NEW_MASTER} (delay: ${MIN_DELAY}s)"
# 3. 停止从库复制
mysql -h${NEW_MASTER} -e "STOP SLAVE; RESET SLAVE ALL;"
# 4. 提升为写库
mysql -h${NEW_MASTER} -e "SET GLOBAL read_only = OFF; SET GLOBAL super_read_only = OFF;"
# 5. 切换VIP
echo "Switching VIP to new master..."
ssh ${NEW_MASTER} "ip addr add ${VIP}/24 dev eth0"
ssh ${MASTER_HOST} "ip addr del ${VIP}/24 dev eth0" 2>/dev/null
# 6. 更新其他从库的主库指向
for slave in ${SLAVE_HOSTS[@]}; do
if [ "$slave" != "$NEW_MASTER" ]; then
mysql -h${slave} -e "STOP SLAVE;"
mysql -h${slave} -e "CHANGE MASTER TO MASTER_HOST='${NEW_MASTER}', MASTER_AUTO_POSITION=1;"
mysql -h${slave} -e "START SLAVE;"
fi
done
# 7. 发送通知
echo "Failover completed. New master: ${NEW_MASTER}" | mail -s "MySQL Failover" admin@company.com
echo "Failover completed successfully!"
六、最佳实践与检查清单
6.1 备份检查清单
┌─────────────────────────────────────────────────────────────────────┐
│ 数据库备份检查清单 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【日常检查】 │
│ □ 1. 检查备份任务是否成功完成 │
│ □ 2. 检查备份文件大小是否合理 │
│ □ 3. 检查备份日志是否有错误 │
│ □ 4. 检查Binlog是否正常备份 │
│ │
│ 【周检查】 │
│ □ 1. 验证备份文件可恢复性 │
│ □ 2. 检查备份存储空间使用率 │
│ □ 3. 检查异地备份同步状态 │
│ □ 4. 更新恢复文档 │
│ │
│ 【月检查】 │
│ □ 1. 执行完整恢复演练 │
│ □ 2. 测试Binlog闪回功能 │
│ □ 3. 检查备份保留策略执行情况 │
│ □ 4. 更新灾难恢复预案 │
│ │
│ 【季度检查】 │
│ □ 1. 跨机房恢复演练 │
│ □ 2. 评估RTO/RPO目标达成情况 │
│ □ 3. 优化备份策略 │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.2 常见错误与解决方案
| 错误 | 后果 | 解决方案 |
|---|---|---|
| 只有本地备份 | 本地灾难时数据全失 | 实施异地备份 |
| 从不测试恢复 | 恢复时发现问题 | 定期恢复演练 |
| 备份文件损坏 | 无法恢复数据 | 定期验证备份完整性 |
| 权限过大 | 误操作风险 | 最小权限原则 |
| 无Binlog备份 | 无法时间点恢复 | 实时Binlog备份 |
| 单点恢复 | 恢复时间长 | 并行恢复方案 |
系列上一篇:数据库监控与性能分析体系
系列下一篇:数据库安全与合规
知识点测试
读完文章了?来测试一下你对知识点的掌握程度吧!
评论区
使用 GitHub 账号登录后即可发表评论,支持 Markdown 格式。
如果评论系统无法加载,请确保:
- 您的网络可以访问 GitHub
- giscus GitHub App 已安装到仓库
- 仓库已启用 Discussions 功能