灾备与容灾方案
一句话:灾备不是”再搭一套一样的”,而是根据 RTO/RPO 要求选择合适的容灾等级——从定期备份到同城双活到异地多活,成本和恢复能力递增。
容灾等级
| 等级 | 架构 | RTO | RPO | 成本倍数 | 适用 |
|---|
| L1 | 定期备份 | 天级 | 天级 | 1x | 非核心 |
| L2 | 异地备份 | 小时级 | 小时级 | 1.2x | 准核心 |
| L3 | 同城主备 | 分钟级 | 秒级 | 2x | 核心 |
| L4 | 同城双活 | 秒级 | 0 | 2x | 高核心 |
| L5 | 异地双活 | 分钟级 | 秒级 | 3x | 超核心 |
| L6 | 全球多活 | 0 | 0 | Nx | 全球服务 |
数据备份策略
备份层次
L1: 本地快照(秒级,同集群)
├── PV 快照
└── DB 逻辑备份
L2: 同城备份(分钟级,跨 AZ)
├── DB 异步复制
└── 对象存储跨 AZ 复制
L3: 异地备份(小时级,跨 Region)
├── DB binlog 同步
└── 对象存储跨 Region 复制
L4: 冷备份归档(天级,离线介质)
└── 归档存储 / 磁带
K8s 备份:Velero
# 安装 Velero
velero install \
--provider aws \
--bucket velero-backups \
--backup-location-config region=cn-beijing \
--snapshot-location-config region=cn-beijing
# 定时备份
velero schedule create daily-backup \
--schedule="0 2 * * *" \
--include-namespaces production \
--ttl 720h # 保留 30 天
# 恢复
velero restore create --from-backup daily-backup-20260803
# 跨集群迁移
# Cluster A: velero backup create migration-backup --include-namespaces app
# Cluster B: velero restore create --from-backup migration-backup
MySQL 备份恢复
# 全量备份(xtrabackup)
xtrabackup --backup --target-dir=/backup/full \
--user=root --password=xxx
# 增量备份
xtrabackup --backup --target-dir=/backup/inc1 \
--incremental-basedir=/backup/full \
--user=root --password=xxx
# 恢复流程
# 1. 准备全量备份
xtrabackup --prepare --target-dir=/backup/full
# 2. 应用增量
xtrabackup --prepare --target-dir=/backup/full \
--incremental-dir=/backup/inc1
# 3. 恢复数据
xtrabackup --copy-back --target-dir=/backup/full --datadir=/var/lib/mysql
# 4. PITR(基于 binlog 回放到指定时间点)
mysqlbinlog --start-datetime="2026-08-03 14:00:00" \
--stop-datetime="2026-08-03 14:05:00" \
mysql-bin.000123 | mysql -u root -p
同城双活架构
graph TD
LB[全局负载均衡<br/>DNS/GSLB] --> AZ1[AZ-1<br/>主集群]
LB --> AZ2[AZ-2<br/>备集群]
AZ1 --> DB1[(MySQL Master<br/>AZ-1)]
AZ2 --> DB2[(MySQL Slave<br/>AZ-2)]
DB1 -->|同步复制| DB2
AZ1 --> REDIS1[(Redis Master<br/>AZ-1)]
AZ2 --> REDIS2[(Redis Slave<br/>AZ-2)]
REDIS1 -->|异步复制| REDIS2
AZ1 --> ES1[(ES Master<br/>AZ-1)]
AZ2 --> ES2[(ES Replica<br/>AZ-2)]
同城双活关键设计:
1. 流量层: GSLB 按权重分流到两个 AZ
2. 计算层: 两个 AZ 同时服务(Active-Active)
3. 数据层: MySQL 半同步复制(RPO ≈ 0)
4. 缓存层: Redis 主从(切换时有秒级数据差异)
5. 消息层: Kafka 跨 AZ 副本
故障切换:
AZ-1 故障 → GSLB 将流量全切到 AZ-2 → MySQL 提升 Slave 为 Master
RTO: 1-5 分钟
RPO: 0(半同步)或秒级(异步)
异地多活架构
graph TD
GSLB[全局 DNS] --> R1[北京 Region]
GSLB --> R2[上海 Region]
R1 --> U1[单元1<br/>北京用户]
R2 --> U2[单元2<br/>上海用户]
R1 --> DSYNC1[数据同步<br/>DTS/Canal]
R2 --> DSYNC2[数据同步<br/>DTS/Canal]
DSYNC1 <-->|异步双向| DSYNC2
异地多活(单元化架构):
1. 按用户维度分片(user_id 路由到单元)
2. 每个单元自包含(应用 + DB + 缓存)
3. 跨单元数据异步同步(最终一致)
4. 单元故障 → 切换到备单元
RTO: 分钟级(DNS 切换 + 数据校验)
RPO: 秒级(异步同步延迟)
核心难点:
- 数据一致性(跨 Region 延迟大,不能用同步复制)
- 冲突解决(双写冲突、幂等)
- 路由正确性(用户必须路由到正确单元)
灾备切换流程
graph TD
DECISION[决策切换] --> NOTIFY[通知相关方]
NOTIFY --> STOP1[停止主集群写入<br/>设置只读]
STOP1 --> SYNC[等待数据同步完成<br/>确认 RPO=0]
SYNC --> PROMOTE[提升备集群为主<br/>DB failover]
PROMOTE --> DNS[DNS 切换<br/>指向备集群]
DNS --> VERIFY[验证服务<br/>SLI 正常]
VERIFY -->|正常| DONE[切换完成<br/>进入恢复期]
VERIFY -->|异常| ROLLBACK[回滚<br/>切回主集群]
灾备切换 Checklist
## 灾备切换 Checklist
### 切换前
- [ ] 确认灾备集群数据同步正常
- [ ] 通知所有相关方(开发/业务/客服)
- [ ] 确认切换窗口(低峰期)
- [ ] 准备回滚方案
### 切换中
- [ ] 主集群设置只读(停止写入)
- [ ] 等待数据同步延迟归零
- [ ] 提升灾备 DB 为 Master
- [ ] 更新 DNS / GSLB 指向
- [ ] 验证服务健康(SLI 正常)
- [ ] 验证数据完整性
### 切换后
- [ ] 监控告警确认无异常
- [ ] 反向数据同步配置(备 → 主)
- [ ] 通知恢复完成
- [ ] 安排原主集群修复
- [ ] 回切计划(原集群修复后)
容灾演练
容灾演练频率:
桌面推演: 每月(纸上谈兵,走流程)
技术演练: 每季度(实际切换但不切流量)
全量演练: 每半年(真实切换 + 流量验证)
演练度量:
- 实际 RTO vs 目标 RTO
- 实际 RPO vs 目标 RPO
- 切换成功率
- 数据一致性验证结果
常见问题 / 坑点
| 问题 | 原因 | 解决方案 |
|---|
| 备份没验证过 | 备份了但没恢复测试 | 定期恢复演练 |
| 切换后数据不一致 | 异步复制延迟 | 等延迟归零再切换 + 数据校验 |
| DNS 切换太慢 | TTL 太长 | 短 TTL + 预热 + 多 DNS |
| 多活路由错误 | 用户路由到错误单元 | 路由层 + 数据层双重保障 |
| 容灾集群资源不足 | 平时不跑流量 | 定期灌流量验证 + 容量预留 |
关联知识
参考资源
状态