Skip to content

MongoDB 灾难恢复切换

切换准备

1. 灾备架构设计

常见灾备架构

  • 主备模式:一个主集群,一个备用集群,数据异步同步
  • 双活模式:两个活跃集群,数据双向同步,负载均衡
  • 多活模式:多个活跃集群,分布在不同区域,数据同步

架构选择考虑因素

  • 业务对数据一致性的要求
  • 可接受的停机时间
  • 预算和资源限制
  • 数据中心间的网络延迟
  • 合规要求

2. 数据同步配置

同步方式

  • 副本集跨区域部署:主节点在一个区域,副本节点在另一个区域
  • 分片集群跨区域部署:分片分布在不同区域
  • 基于 Oplog 同步:使用 Oplog 进行跨区域数据同步
  • 第三方同步工具:如 MongoDB Atlas Live Migration Service、Debezium 等

同步验证

  • 监控同步延迟
  • 定期验证数据一致性
  • 测试同步中断后的恢复

3. 应用配置

连接配置

  • 使用可切换的连接字符串
  • 配置连接池和重试机制
  • 实现应用级别的故障检测

切换逻辑

  • 设计应用的切换逻辑
  • 实现流量切换机制
  • 考虑部分流量切换(金丝雀发布)

4. 监控和告警

监控指标

  • 主集群状态
  • 同步延迟
  • 备用集群状态
  • 应用连接状态

告警配置

  • 主集群故障告警
  • 同步延迟过高告警
  • 备用集群异常告警
  • 应用连接失败告警

切换流程

1. 计划内切换流程

步骤

  1. 准备阶段

    • 通知相关团队
    • 确认备用集群状态正常
    • 验证数据同步状态
    • 准备回滚计划
  2. 预切换检查

    • 检查主集群和备用集群的健康状态
    • 验证数据一致性
    • 检查应用连接配置
    • 测试备用集群的读写功能
  3. 切换执行

    • 暂停主集群的写入操作(可选,取决于架构)
    • 等待数据同步完成
    • 更新应用连接字符串或流量路由
    • 启动应用并验证功能
  4. 验证阶段

    • 检查应用功能
    • 监控备用集群性能
    • 验证数据一致性
    • 确认用户访问正常
  5. 完成阶段

    • 通知相关团队切换完成
    • 更新文档和配置
    • 进行切换总结和复盘

2. 计划外切换流程

步骤

  1. 故障检测

    • 收到主集群故障告警
    • 验证主集群确实不可用
    • 评估故障影响范围和持续时间
  2. 决策阶段

    • 决定是否执行灾难恢复切换
    • 通知相关团队和管理层
    • 启动灾难恢复流程
  3. 切换执行

    • 停止主集群的相关服务(如果可能)
    • 验证备用集群状态
    • 更新应用连接字符串或流量路由
    • 启动应用
  4. 验证阶段

    • 检查应用核心功能
    • 监控备用集群性能
    • 验证数据一致性
    • 处理可能的问题
  5. 恢复阶段

    • 修复主集群故障
    • 重新同步数据
    • 考虑是否需要切回主集群
    • 更新灾难恢复计划

切换执行

1. 副本集切换

步骤

  1. 检查备用副本集状态
javascript
// 连接到备用副本集
mongo --host standby-primary:27017 --username admin --password password --authenticationDatabase admin

// 检查副本集状态
rs.status()

// 检查同步延迟
rs.status().members.forEach(member => {
  print(`${member.name}: ${member.stateStr}, ${member.replicationLag || 0}ms`);
});
  1. 提升备用副本集为主集群
javascript
// 如果备用副本集是从节点,手动提升为主节点
rs.stepUp()

// 验证主节点状态
rs.status().members.forEach(member => {
  if (member.stateStr === "PRIMARY") {
    print(`Primary is now: ${member.name}`);
  }
});
  1. 更新应用连接字符串

示例连接字符串

mongodb://admin:password@standby-primary:27017,standby-secondary1:27017,standby-secondary2:27017/?replicaSet=rs0&readPreference=primary
  1. 验证应用连接
bash
# 测试应用连接
mongo --host standby-primary:27017 --username appuser --password apppassword --authenticationDatabase mydb --eval "db.getSiblingDB('mydb').mycollection.findOne()"

2. 分片集群切换

步骤

  1. 检查备用分片集群状态
javascript
// 连接到备用 mongos
mongo --host standby-mongos:27017 --username admin --password password --authenticationDatabase admin

// 检查分片集群状态
sh.status()

// 检查每个分片的状态
sh.status().shards.forEach(shard => {
  print(`${shard._id}: ${shard.host}`);
});
  1. 更新应用连接到备用 mongos

示例连接字符串

mongodb://admin:password@standby-mongos1:27017,standby-mongos2:27017,standby-mongos3:27017/?readPreference=primary
  1. 验证分片集群功能
javascript
// 创建测试集合
use test
db.createCollection("test_collection")

// 插入测试数据
db.test_collection.insertOne({ "test": "data" })

// 查询测试数据
db.test_collection.findOne()

// 验证分片功能
sh.enableSharding("test")
sh.shardCollection("test.test_collection", { "_id": 1 })

3. 基于 DNS 的切换

步骤

  1. 更新 DNS 记录
bash
# 示例:使用 AWS Route 53 更新 DNS 记录
aws route53 change-resource-record-sets \
  --hosted-zone-id ZONE_ID \
  --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"mongodb.example.com","Type":"A","TTL":60,"ResourceRecords":[{"Value":"STANDBY_IP"}]}}]}'
  1. 验证 DNS 生效
bash
# 验证 DNS 解析
nslookup mongodb.example.com

# 等待 DNS 传播
watch -n 5 "nslookup mongodb.example.com"
  1. 验证应用连接
bash
# 使用新的 DNS 连接mongo --host mongodb.example.com:27017 --username admin --password password --authenticationDatabase admin --eval "db.serverStatus().version"

切换验证

1. 功能验证

验证内容

  • 应用核心功能
  • 数据读写操作
  • 索引查询
  • 事务功能(如果使用)
  • 备份功能

验证方法

  • 运行应用自动化测试
  • 执行手动测试用例
  • 模拟用户访问场景

2. 性能验证

验证指标

  • 查询响应时间
  • 写入吞吐量
  • CPU 和内存使用率
  • 磁盘 I/O
  • 连接数

验证方法

  • 运行基准测试
  • 监控系统性能
  • 比较切换前后的性能差异

3. 数据一致性验证

验证方法

  • 检查文档计数
  • 抽样比较数据内容
  • 验证索引完整性
  • 检查 Oplog 状态

示例

javascript
// 检查文档计数
db.getSiblingDB('mydb').mycollection.countDocuments()

// 抽样比较数据
for (let i = 0; i < 10; i++) {
  let doc = db.getSiblingDB('mydb').mycollection.find().skip(Math.floor(Math.random() * 1000)).limit(1).next();
  printjson(doc);
}

// 检查索引
 db.getSiblingDB('mydb').mycollection.getIndexes()

4. 监控验证

监控内容

  • 集群状态
  • 同步状态(如果有)
  • 应用连接状态
  • 系统资源使用情况
  • 慢查询

监控工具

  • MongoDB Atlas/Ops Manager
  • Prometheus + Grafana
  • 自定义监控脚本

回滚流程

回滚触发条件

  • 切换后备用集群出现严重故障
  • 数据一致性问题无法解决
  • 应用功能异常无法修复
  • 主集群故障已修复,可以恢复

回滚步骤

  1. 决策阶段

    • 评估回滚的必要性和风险
    • 通知相关团队
    • 准备回滚计划
  2. 回滚执行

    • 暂停应用写入(如果可能)
    • 更新应用连接字符串,切回主集群
    • 启动应用并验证功能
  3. 验证阶段

    • 检查应用功能
    • 监控主集群性能
    • 验证数据一致性
  4. 恢复阶段

    • 修复备用集群问题
    • 重新同步数据
    • 更新灾难恢复计划

最佳实践

1. 定期演练

  • 每年至少进行一次灾难恢复演练
  • 演练包括计划内和计划外切换
  • 记录演练过程和结果
  • 针对演练中发现的问题进行改进

2. 自动化

  • 自动化切换流程,减少人为错误
  • 实现自动化监控和告警
  • 自动化验证切换结果
  • 自动化回滚流程

3. 文档和培训

  • 编写详细的切换文档
  • 培训相关团队成员
  • 建立清晰的沟通机制
  • 明确各角色的责任

4. 监控和告警

  • 建立全面的监控体系
  • 配置合理的告警阈值
  • 实现多级告警机制
  • 定期审查告警配置

5. 数据保护

  • 确保备用集群的数据完整性
  • 定期备份备用集群数据
  • 验证备份恢复功能
  • 考虑数据加密

6. 逐步切换

  • 考虑采用金丝雀发布方式
  • 逐步将流量切换到备用集群
  • 监控每一步的切换结果
  • 准备随时回滚

常见问题(FAQ)

Q1: 灾难恢复切换需要多长时间?

A1: 切换时间取决于多种因素:

  • 切换类型(计划内或计划外)
  • 部署架构
  • 数据量大小
  • 同步延迟
  • 应用切换机制

计划内切换通常需要几分钟到几小时,计划外切换可能需要更长时间,取决于故障检测和决策时间。

Q2: 如何确保切换过程中的数据一致性?

A2: 确保数据一致性的方法:

  • 切换前等待数据同步完成
  • 验证源和目标数据的一致性
  • 使用事务确保数据完整性
  • 实现应用级别的数据验证

Q3: 切换后如何处理主集群?

A3: 切换后主集群的处理:

  • 修复主集群的故障
  • 重新同步数据到主集群
  • 考虑将主集群作为新的备用集群
  • 或根据需要退役主集群

Q4: 如何测试灾难恢复切换?

A4: 测试灾难恢复切换的方法:

  • 进行定期的演练
  • 模拟主集群故障
  • 测试不同场景的切换
  • 验证切换后的系统状态

Q5: 灾难恢复切换的风险有哪些?

A5: 灾难恢复切换的风险:

  • 数据不一致
  • 应用功能异常
  • 性能下降
  • 切换失败
  • 回滚困难

Q6: 如何选择合适的灾难恢复架构?

A6: 选择灾难恢复架构的考虑因素:

  • 业务对数据一致性的要求
  • 可接受的停机时间
  • 预算和资源限制
  • 数据中心间的网络延迟
  • 合规要求

Q7: 如何监控灾难恢复切换过程?

A7: 监控灾难恢复切换过程的方法:

  • 监控集群状态
  • 监控数据同步
  • 监控应用连接
  • 监控系统资源
  • 记录切换日志

Q8: 如何优化灾难恢复切换时间?

A8: 优化切换时间的方法:

  • 实现自动化切换
  • 优化数据同步机制
  • 减少同步延迟
  • 设计高效的应用切换逻辑
  • 提前准备切换资源