Skip to content

MongoDB 分片集群高可用性

分片集群架构设计

核心组件设计

  1. 配置服务器(Config Servers)

    • 推荐配置:3 个成员的复制集
    • 存储分片集群的元数据
    • 确保配置数据的一致性和高可用性
  2. 分片(Shards)

    • 每个分片都是一个复制集
    • 推荐配置:3 个或 5 个成员的复制集
    • 处理实际的数据读写操作
  3. 查询路由器(mongos)

    • 部署多个 mongos 实例
    • 客户端连接到 mongos 而非直接连接到分片
    • 路由查询到正确的分片

高可用架构示例

[客户端应用]

[负载均衡器]

[mongos 1] [mongos 2] [mongos 3]
    ↓         ↓         ↓
[配置服务器复制集(3 个成员)]

[分片 1 复制集(3 个成员)] [分片 2 复制集(3 个成员)] [分片 3 复制集(3 个成员)]

配置服务器高可用性

配置服务器复制集部署

  1. 初始化配置服务器复制集

    bash
    # 启动配置服务器节点
    mongod --configsvr --replSet csrs --dbpath /data/configdb1 --port 27019 --fork --logpath /var/log/mongodb/configdb1.log
    mongod --configsvr --replSet csrs --dbpath /data/configdb2 --port 27020 --fork --logpath /var/log/mongodb/configdb2.log
    mongod --configsvr --replSet csrs --dbpath /data/configdb3 --port 27021 --fork --logpath /var/log/mongodb/configdb3.log
    
    # 初始化复制集
    mongo --port 27019 --eval "
      rs.initiate({
        _id: 'csrs',
        configsvr: true,
        members: [
          { _id: 0, host: 'config1:27019' },
          { _id: 1, host: 'config2:27020' },
          { _id: 2, host: 'config3:27021' }
        ]
      })
    "
  2. 配置服务器监控

    javascript
    // 检查配置服务器状态
    use config
    db.serverStatus()
    
    // 检查复制集状态
    rs.status()
    
    // 检查配置数据完整性
    db.chunks.count()
    db.collections.count()
    db.databases.count()

配置服务器故障处理

  1. 单个配置服务器故障

    • 复制集自动处理,其他成员继续提供服务
    • 无需手动干预,只需监控故障成员的恢复
  2. 多个配置服务器故障

    • 若多数成员故障,配置服务器复制集将不可用
    • 此时 mongos 无法获取或更新元数据
    • 建议:
      javascript
      // 检查复制集状态
      rs.status()
      
      // 恢复故障成员
      // 1. 修复硬件或网络问题
      // 2. 重启 mongod 实例
      // 3. 等待复制集自动恢复

分片高可用性

分片复制集配置

  1. 初始化分片复制集

    bash
    # 启动分片 1 节点
    mongod --shardsvr --replSet shard1 --dbpath /data/shard1a --port 27018 --fork --logpath /var/log/mongodb/shard1a.log
    mongod --shardsvr --replSet shard1 --dbpath /data/shard1b --port 27022 --fork --logpath /var/log/mongodb/shard1b.log
    mongod --shardsvr --replSet shard1 --dbpath /data/shard1c --port 27023 --fork --logpath /var/log/mongodb/shard1c.log
    
    # 初始化分片 1 复制集
    mongo --port 27018 --eval "
      rs.initiate({
        _id: 'shard1',
        members: [
          { _id: 0, host: 'shard1a:27018' },
          { _id: 1, host: 'shard1b:27022' },
          { _id: 2, host: 'shard1c:27023' }
        ]
      })
    "
  2. 添加分片到集群

    javascript
    // 连接到 mongos
    mongo --port 27017
    
    // 添加分片
    sh.addShard("shard1/shard1a:27018,shard1b:27022,shard1c:27023")
    sh.addShard("shard2/shard2a:27024,shard2b:27025,shard2c:27026")
    sh.addShard("shard3/shard3a:27027,shard3b:27028,shard3c:27029")

分片故障处理

  1. 分片主节点故障

    • 复制集自动选举新的主节点
    • 选举过程通常在 10 秒内完成
    • 应用程序可能会短暂中断写入操作
  2. 分片从节点故障

    • 不影响分片的可用性
    • 复制集仍可处理读写操作
    • 监控复制延迟,确保剩余节点能够处理负载
  3. 整个分片故障

    • 若整个分片不可用,该分片上的数据将无法访问
    • 建议:
      javascript
      // 检查分片状态
      sh.status()
      
      // 检查受影响的集合
      db.collection.getShardDistribution()
      
      // 恢复故障分片
      // 1. 修复硬件或网络问题
      // 2. 重启分片成员
      // 3. 等待复制集恢复

mongos 高可用性

mongos 部署策略

  1. 多 mongos 实例部署

    • 在多个服务器上部署 mongos 实例
    • 推荐每个应用服务器部署一个 mongos 实例
    • 或使用负载均衡器分发客户端连接
  2. 启动 mongos

    bash
    # 启动 mongos
    mongos --configdb csrs/config1:27019,config2:27020,config3:27021 --port 27017 --fork --logpath /var/log/mongodb/mongos.log
    mongos --configdb csrs/config1:27019,config2:27020,config3:27021 --port 27030 --fork --logpath /var/log/mongodb/mongos2.log

mongos 故障处理

  1. 单个 mongos 故障

    • 客户端自动连接到其他可用的 mongos 实例
    • 建议在客户端实现 mongos 实例的自动发现和故障转移
  2. 所有 mongos 实例故障

    • 客户端无法连接到分片集群
    • 建议:
      bash
      # 重启 mongos 实例
      mongos --configdb csrs/config1:27019,config2:27020,config3:27021 --port 27017 --fork --logpath /var/log/mongodb/mongos.log
      
      # 检查 mongos 状态
      mongo --port 27017 --eval "db.adminCommand({ ping: 1 })"

分片集群故障转移

自动故障转移

  1. 复制集自动故障转移

    • 当主节点不可用时,复制集会自动选举新的主节点
    • 适用于配置服务器复制集和分片复制集
  2. mongos 自动故障转移

    • 客户端需要实现 mongos 实例的自动发现和故障转移
    • 建议使用负载均衡器或客户端驱动的自动重连机制

手动故障转移

  1. 手动切换复制集主节点

    javascript
    // 在主节点上执行
    rs.stepDown()
  2. 手动恢复故障分片

    javascript
    // 检查分片状态
    sh.status()
    
    // 检查分片复制集状态
    use admin

db.runCommand({ replSetGetStatus: 1, shard: "shard1" })


## 监控和告警

### 关键监控指标

1. **mongos 监控指标**
- 连接数:`db.serverStatus().connections`
- 查询速率:`db.serverStatus().opcounters`
- 路由耗时:`db.serverStatus().metrics.query.router`
- 配置服务器连接状态

2. **配置服务器监控指标**
- 复制集状态:`rs.status()`
- 复制延迟:`rs.printSlaveReplicationInfo()`
- 配置数据完整性

3. **分片监控指标**
- 复制集状态:`rs.status()`
- 复制延迟:`rs.printSlaveReplicationInfo()`
- 数据分布:`db.collection.getShardDistribution()`
- 分片负载:`db.serverStatus().globalLock`

### 告警设置

1. **mongos 告警**
- 连接数超过阈值
- mongos 实例不可用
- 配置服务器连接失败

2. **配置服务器告警**
- 复制集状态异常
- 复制延迟超过阈值
- 配置服务器成员不可用

3. **分片告警**
- 复制集状态异常
- 复制延迟超过阈值
- 分片成员不可用
- 单个分片负载过高

## 备份和恢复

### 分片集群备份策略

1. **分片级备份**
- 对每个分片的复制集进行备份
- 使用 `mongodump` 或文件系统快照
- 备份 oplog 用于时间点恢复

2. **配置服务器备份**
- 备份配置服务器复制集
- 确保配置数据的完整性

3. **一致性备份**
- 使用 `--oplog` 选项进行一致性备份
- 或在所有分片上使用相同的时间点进行备份

### 分片集群恢复流程

1. **恢复单个分片**
- 恢复分片复制集
- 确保分片数据与其他分片一致
- 更新分片集群元数据

2. **恢复整个集群**
- 恢复配置服务器复制集
- 恢复每个分片的复制集
- 启动 mongos 实例
- 验证集群状态

## 扩容和缩容

### 分片集群扩容

1. **添加新分片**
```javascript
// 连接到 mongos
mongo --port 27017

// 添加新分片
sh.addShard("shard4/shard4a:27031,shard4b:27032,shard4c:27033")

// 开启平衡器
sh.enableBalancing("mydb.mycollection")

// 检查平衡器状态
sh.getBalancerState()
  1. 数据重平衡
    • MongoDB 自动将数据迁移到新分片
    • 监控平衡器状态
    • 调整平衡器窗口

分片集群缩容

  1. 移除分片

    javascript
    // 连接到 mongos
    mongo --port 27017
    
    // 移除分片
    sh.removeShard("shard4")
    
    // 检查移除状态
    sh.status()
  2. 数据迁移

    • MongoDB 自动将数据从待移除分片迁移到其他分片
    • 监控迁移进度
    • 确认数据迁移完成

最佳实践

架构设计最佳实践

  1. 奇数个复制集成员

    • 配置服务器复制集:3 个成员
    • 每个分片复制集:3 个或 5 个成员
    • 避免使用仲裁者
  2. 跨区域部署

    • 将复制集成员分布在不同的数据中心
    • 提高容灾能力
    • 考虑使用多区域负载均衡
  3. 适当的硬件配置

    • 配置服务器:中等配置即可
    • 分片:根据数据量和负载选择适当的硬件
    • mongos:轻量级,可部署在应用服务器上

运维最佳实践

  1. 定期监控

    • 配置全面的监控和告警
    • 定期检查集群状态
    • 分析性能指标
  2. 定期备份

    • 制定备份策略
    • 定期测试恢复流程
    • 确保备份数据的完整性
  3. 定期演练

    • 定期进行故障演练
    • 测试自动故障转移
    • 验证恢复流程
  4. 升级策略

    • 遵循推荐的升级顺序:配置服务器 → 分片 → mongos
    • 在测试环境验证升级
    • 制定回滚计划

常见问题(FAQ)

Q1: 分片集群最少需要多少台服务器?

A1: 推荐的最小配置:

  • 3 台服务器用于配置服务器复制集
  • 3 台服务器用于第一个分片的复制集
  • 1 台服务器用于 mongos 实例
  • 总共 7 台服务器

Q2: 如何选择分片键?

A2: 选择分片键的建议:

  • 高基数:分片键值的分布范围广
  • 低频率:分片键值的更新频率低
  • 随机性:写入操作分布在多个分片上
  • 查询模式:考虑常用的查询模式

Q3: 如何处理热点分片?

A3: 处理热点分片的方法:

  • 优化分片键设计
  • 使用更细粒度的分片键
  • 考虑使用哈希分片
  • 监控分片负载,及时扩容

Q4: 如何监控分片集群的平衡器?

A4: 监控平衡器的方法:

javascript
// 检查平衡器状态
sh.getBalancerState()

// 检查平衡器运行状态
sh.isBalancerRunning()

// 查看平衡器日志
db.adminCommand({ logs: 1, logComponent: "sharding" })

Q5: 分片集群的写入关注点应该如何设置?

A5: 写入关注点建议:

  • 生产环境:{ w: "majority" }
  • 确保数据写入到大多数节点
  • 平衡一致性和性能需求

Q6: 如何处理分片集群中的网络分区?

A6: 处理网络分区的方法:

  • 设计跨区域架构
  • 配置适当的投票成员
  • 监控网络连接
  • 准备手动干预方案

Q7: 如何验证分片集群的完整性?

A7: 验证集群完整性的方法:

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

// 检查配置服务器状态
use config
db.serverStatus()

// 检查分片状态
db.adminCommand({ replSetGetStatus: 1, shard: "shard1" })

// 验证数据分布
db.collection.getShardDistribution()

Q8: 如何优化分片集群的查询性能?

A8: 优化查询性能的方法:

  • 创建合适的索引
  • 优化查询语句
  • 考虑使用覆盖索引
  • 避免跨分片查询
  • 优化分片键设计

Q9: 如何处理分片集群中的长时间运行操作?

A9: 处理长时间运行操作的方法:

javascript
// 查看当前操作
db.currentOp()

// 终止长时间运行的操作
db.killOp(opid)

// 优化长时间运行的查询
// 1. 分析查询执行计划
// 2. 创建适当的索引
// 3. 考虑使用分片键查询

Q10: 如何升级分片集群?

A10: 升级分片集群的步骤:

  1. 升级配置服务器复制集
  2. 升级每个分片的复制集
  3. 升级 mongos 实例
  4. 验证集群状态
  5. 更新功能兼容性版本