Skip to content

MongoDB 复制集原理

复制集基本概念

复制集定义

  1. 基本概念

    • 一组维护相同数据集的MongoDB实例
    • 包含一个主节点(Primary)和多个从节点(Secondary)
    • 提供数据冗余和高可用性
    • 支持自动故障转移
  2. 主要组件

    • 主节点:处理所有写操作和读操作(默认)
    • 从节点:复制主节点数据,提供读扩展
    • 仲裁节点:仅参与选举,不存储数据
    • 隐藏节点:不参与选举,不接受客户端请求
    • 延迟节点:故意落后于主节点的从节点
  3. 主要功能

    • 数据冗余
    • 高可用性
    • 读扩展
    • 灾难恢复

复制集架构

  1. 常见架构

    • 3节点架构:1主2从,最常用的生产架构
    • 5节点架构:1主4从,更高的可用性
    • 带仲裁节点架构:2主2从1仲裁,节省资源
  2. 节点分布

    • 跨可用区部署
    • 跨区域部署
    • 考虑网络延迟
  3. 架构选择

    • 根据业务需求选择
    • 考虑可用性要求
    • 考虑成本因素

数据复制机制

Oplog 机制

  1. Oplog 概念

    • Operation Log(操作日志)的简称
    • 存储在local.oplog.rs集合中
    • 固定大小的集合
    • 循环写入,旧记录会被新记录覆盖
  2. Oplog 格式

    javascript
    {
      "ts" : Timestamp(1609459200, 1),
      "t" : NumberLong(1),
      "h" : NumberLong("-1234567890123456789"),
      "v" : 2,
      "op" : "i",  // 操作类型:i=insert, u=update, d=delete, c=command
      "ns" : "test.collection",  // 命名空间
      "o" : { "_id" : ObjectId("5f9f1b0c0a0b0c0d0e0f0a0b"), "name" : "test" }  // 操作内容
    }
  3. Oplog 大小

    • 默认大小:
      • 64位Linux/macOS:物理内存的5%
      • Windows:50GB
    • 建议大小:能存储24-48小时的操作
    • 可通过oplogSizeMB参数调整

复制过程

  1. 主节点写操作

    • 客户端发送写请求到主节点
    • 主节点执行写操作
    • 主节点将操作记录到oplog
  2. 从节点复制过程

    • 从节点定期从主节点拉取oplog
    • 从节点在自己的数据集上应用oplog
    • 从节点确认复制完成
  3. 复制延迟

    • 从节点与主节点之间的时间差
    • 影响因素:网络延迟、主节点负载、从节点负载
    • 建议监控复制延迟,设置告警阈值
  4. 复制确认

    • writeConcern参数控制写操作的确认级别
    • w:1:主节点确认
    • w:majority:大多数节点确认
    • w:all:所有节点确认

初始同步

  1. 初始同步触发条件

    • 新节点加入复制集
    • 从节点数据损坏
    • 从节点与主节点oplog差距过大
  2. 初始同步过程

    • 克隆所有数据库(除local数据库)
    • 应用oplog中的操作
    • 建立索引
  3. 优化初始同步

    • 确保主节点有足够的资源
    • 考虑使用文件系统快照初始化从节点
    • 监控初始同步进度
  4. 初始同步监控

    javascript
    // 查看初始同步状态
    rs.status().members.forEach(function(member) {
      if (member.stateStr === "STARTUP2") {
        print(member.name + " is initial syncing");
      }
    });

选举机制

选举触发条件

  1. 主节点不可用

    • 从节点在electionTimeoutMillis时间内未收到主节点的心跳
    • 默认electionTimeoutMillis为10秒
  2. 人工触发

    • 使用rs.stepDown()命令
    • 使用rs.reconfig()命令修改复制集配置
  3. 选举过程

    • 从节点发起选举
    • 收集其他节点的投票
    • 获得多数票的节点成为新主节点
    • 新主节点通知所有节点

选举规则

  1. 选举资格

    • 节点状态为HEALTHY
    • 节点可以与多数节点通信
    • 节点的数据足够新
    • 节点优先级大于0(除非是唯一候选节点)
  2. 投票规则

    • 每个节点只有一票
    • 节点不能给自己投票
    • 节点会投票给数据最新的节点
    • 节点会投票给优先级高的节点
  3. 多数票定义

    • 对于n个节点的复制集,多数票为(n/2)+1
    • 3节点复制集:需要2票
    • 5节点复制集:需要3票

选举优化

  1. 节点优先级

    • 通过priority参数调整节点优先级
    • 优先级高的节点更容易成为主节点
    • 范围:0-100
  2. 选举超时调整

    • 通过electionTimeoutMillis参数调整
    • 较短的超时时间导致更快的故障转移
    • 但可能导致不必要的选举
  3. ** heartbeat 配置**:

    • 通过heartbeatIntervalMillis参数调整
    • 默认2秒
    • 较短的心跳间隔可以更快地检测到故障

复制集成员角色

核心角色

  1. 主节点(Primary)

    • 唯一接受写操作的节点
    • 维护oplog
    • 向从节点发送心跳
    • 参与选举
  2. 从节点(Secondary)

    • 复制主节点数据
    • 可以接受读操作(通过readPreference)
    • 参与选举
    • 向其他节点发送心跳
  3. 仲裁节点(Arbiter)

    • 不存储数据
    • 不参与数据复制
    • 仅参与选举
    • 节省资源

特殊角色

  1. 隐藏节点(Hidden)

    • 不参与选举
    • 不接受客户端请求
    • 用于备份、报告等
    • 通过hidden参数设置
  2. 延迟节点(Delayed)

    • 故意落后于主节点
    • 用于恢复误删除数据
    • 通过slaveDelay参数设置
    • 通常同时设置为隐藏节点
  3. 投票节点(Voting)

    • 参与选举投票
    • 通过votes参数设置
    • 最多50个投票节点
    • 建议投票节点数不超过7个
  4. 非投票节点(Non-Voting)

    • 不参与选举投票
    • 可以是从节点或隐藏节点
    • 通过votes: 0设置

角色转换

  1. 主从转换

    • 自动故障转移时
    • 手动执行rs.stepDown()时
    • 主节点不可用时
  2. 角色配置

    javascript
    // 配置节点角色
    cfg = rs.conf()
    cfg.members[0].priority = 100  // 主节点优先
    cfg.members[1].priority = 50   // 从节点次优先
    cfg.members[2].priority = 0    // 非投票节点
    cfg.members[3].hidden = true   // 隐藏节点
    cfg.members[3].slaveDelay = 3600  // 延迟1小时
    rs.reconfig(cfg)

复制集读偏好

读偏好类型

  1. primary

    • 仅从主节点读取(默认)
    • 保证数据一致性
    • 适用于对数据一致性要求高的场景
  2. primaryPreferred

    • 优先从主节点读取
    • 主节点不可用时从从节点读取
    • 适用于大多数场景
  3. secondary

    • 仅从从节点读取
    • 提供读扩展
    • 可能读取到过期数据
  4. secondaryPreferred

    • 优先从从节点读取
    • 从节点不可用时从主节点读取
    • 适用于读密集型场景
  5. nearest

    • 从网络延迟最低的节点读取
    • 可以是主节点或从节点
    • 适用于对延迟敏感的场景

读偏好配置

  1. 客户端配置

    javascript
    // Node.js 驱动示例
    const client = new MongoClient(uri, {
      readPreference: 'secondaryPreferred',
      readConcern: { level: 'majority' }
    });
  2. 连接字符串配置

    mongodb://host1:27017,host2:27017,host3:27017/?readPreference=secondaryPreferred
  3. 集合级别配置

    javascript
    // 集合级别读偏好
    db.collection.find().readPref('secondaryPreferred')

读关注(Read Concern)

  1. 读关注级别

    • local:读取节点上的最新数据
    • available:读取可用数据
    • majority:读取已被大多数节点确认的数据
    • linearizable:线性izable读,确保读取到最新数据
    • snapshot:读取特定时间点的数据
  2. 读关注与读偏好结合

    javascript
    // 读取已被大多数节点确认的从节点数据
    db.collection.find().readPref('secondaryPreferred').readConcern({ level: 'majority' })

复制集监控

监控指标

  1. 复制延迟

    javascript
    // 查看复制延迟
    rs.printSecondaryReplicationInfo()
  2. Oplog 状态

    javascript
    // 查看oplog状态
    rs.printReplicationInfo()
  3. 复制集状态

    javascript
    // 查看复制集状态
    rs.status()
  4. 节点状态

    javascript
    // 查看节点状态
    rs.status().members.forEach(function(member) {
      print(member.name + ": " + member.stateStr);
    });

常见问题排查

  1. 复制延迟高

    • 检查网络连接
    • 检查主节点负载
    • 检查从节点负载
    • 检查oplog大小
    • 检查从节点是否有长时间运行的操作
  2. 选举失败

    • 检查节点健康状态
    • 检查网络连接
    • 检查多数节点是否可达
    • 检查数据一致性
  3. 从节点不同步

    • 检查从节点状态
    • 检查oplog是否被覆盖
    • 考虑重新初始化从节点
    • 检查防火墙设置

最佳实践

架构设计

  1. 节点数量

    • 建议使用奇数节点(3、5、7)
    • 避免使用偶数节点(除非有仲裁节点)
    • 最多50个节点
  2. 分布部署

    • 跨可用区部署
    • 考虑网络延迟
    • 避免单点故障
  3. 硬件配置

    • 所有节点使用相同的硬件配置
    • 确保足够的存储空间
    • 考虑使用SSD存储

配置优化

  1. Oplog 大小

    • 建议能存储24-48小时的操作
    • 根据写入负载调整
    • 定期监控oplog窗口
  2. 选举配置

    • 根据业务需求调整electionTimeoutMillis
    • 合理设置节点优先级
    • 考虑使用heartbeatIntervalMillis
  3. 读偏好配置

    • 根据业务需求选择合适的读偏好
    • 考虑结合readConcern使用
    • 监控读操作分布

日常维护

  1. 定期检查

    • 检查复制集状态
    • 检查复制延迟
    • 检查oplog状态
    • 检查节点健康状态
  2. 备份策略

    • 定期备份数据
    • 考虑使用延迟节点
    • 测试恢复流程
  3. 升级策略

    • 使用滚动升级
    • 先升级从节点,再升级主节点
    • 监控升级过程

常见问题(FAQ)

Q1: 复制集的最小节点数是多少?

A1: 复制集的最小节点数是1(单节点),但这没有高可用性。建议生产环境使用至少3个节点(1主2从),以提供高可用性和自动故障转移。

Q2: 仲裁节点的作用是什么?

A2: 仲裁节点的主要作用是参与选举,帮助形成多数派,从而实现自动故障转移。仲裁节点不存储数据,资源消耗低,适合在资源有限的环境中使用。

Q3: 如何处理复制延迟高的问题?

A3: 处理复制延迟高的方法:

  • 检查网络连接质量
  • 确保从节点硬件配置与主节点匹配
  • 检查从节点是否有长时间运行的操作
  • 增大oplog容量
  • 调整写关注级别
  • 考虑使用延迟节点进行备份和恢复

Q4: 如何手动触发选举?

A4: 手动触发选举的方法:

  • 使用rs.stepDown()命令让当前主节点下台
  • 使用rs.reconfig()命令修改复制集配置
  • 重启主节点

Q5: 隐藏节点有什么用途?

A5: 隐藏节点的主要用途:

  • 用于备份操作,避免影响生产流量
  • 用于报表查询,隔离分析负载
  • 用于升级测试,降低风险
  • 作为延迟节点,用于恢复误删除数据

Q6: 读偏好和读关注有什么区别?

A6: 读偏好和读关注的区别:

  • 读偏好(readPreference):决定从哪个节点读取数据
  • 读关注(readConcern):决定读取什么级别的数据一致性
  • 两者可以结合使用,提供更精细的读控制

Q7: 如何确保读取到最新的数据?

A7: 确保读取到最新数据的方法:

  • 使用readPreference: 'primary'(默认)
  • 结合使用readConcern:
  • 对于关键操作,可以使用writeConcern: { w: 'majority' }和readConcern: