外观
MongoDB 复制集原理
复制集基本概念
复制集定义
基本概念:
- 一组维护相同数据集的MongoDB实例
- 包含一个主节点(Primary)和多个从节点(Secondary)
- 提供数据冗余和高可用性
- 支持自动故障转移
主要组件:
- 主节点:处理所有写操作和读操作(默认)
- 从节点:复制主节点数据,提供读扩展
- 仲裁节点:仅参与选举,不存储数据
- 隐藏节点:不参与选举,不接受客户端请求
- 延迟节点:故意落后于主节点的从节点
主要功能:
- 数据冗余
- 高可用性
- 读扩展
- 灾难恢复
复制集架构
常见架构:
- 3节点架构:1主2从,最常用的生产架构
- 5节点架构:1主4从,更高的可用性
- 带仲裁节点架构:2主2从1仲裁,节省资源
节点分布:
- 跨可用区部署
- 跨区域部署
- 考虑网络延迟
架构选择:
- 根据业务需求选择
- 考虑可用性要求
- 考虑成本因素
数据复制机制
Oplog 机制
Oplog 概念:
- Operation Log(操作日志)的简称
- 存储在local.oplog.rs集合中
- 固定大小的集合
- 循环写入,旧记录会被新记录覆盖
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" } // 操作内容 }Oplog 大小:
- 默认大小:
- 64位Linux/macOS:物理内存的5%
- Windows:50GB
- 建议大小:能存储24-48小时的操作
- 可通过oplogSizeMB参数调整
- 默认大小:
复制过程
主节点写操作:
- 客户端发送写请求到主节点
- 主节点执行写操作
- 主节点将操作记录到oplog
从节点复制过程:
- 从节点定期从主节点拉取oplog
- 从节点在自己的数据集上应用oplog
- 从节点确认复制完成
复制延迟:
- 从节点与主节点之间的时间差
- 影响因素:网络延迟、主节点负载、从节点负载
- 建议监控复制延迟,设置告警阈值
复制确认:
- writeConcern参数控制写操作的确认级别
- w:1:主节点确认
- w:majority:大多数节点确认
- w:all:所有节点确认
初始同步
初始同步触发条件:
- 新节点加入复制集
- 从节点数据损坏
- 从节点与主节点oplog差距过大
初始同步过程:
- 克隆所有数据库(除local数据库)
- 应用oplog中的操作
- 建立索引
优化初始同步:
- 确保主节点有足够的资源
- 考虑使用文件系统快照初始化从节点
- 监控初始同步进度
初始同步监控:
javascript// 查看初始同步状态 rs.status().members.forEach(function(member) { if (member.stateStr === "STARTUP2") { print(member.name + " is initial syncing"); } });
选举机制
选举触发条件
主节点不可用:
- 从节点在electionTimeoutMillis时间内未收到主节点的心跳
- 默认electionTimeoutMillis为10秒
人工触发:
- 使用rs.stepDown()命令
- 使用rs.reconfig()命令修改复制集配置
选举过程:
- 从节点发起选举
- 收集其他节点的投票
- 获得多数票的节点成为新主节点
- 新主节点通知所有节点
选举规则
选举资格:
- 节点状态为HEALTHY
- 节点可以与多数节点通信
- 节点的数据足够新
- 节点优先级大于0(除非是唯一候选节点)
投票规则:
- 每个节点只有一票
- 节点不能给自己投票
- 节点会投票给数据最新的节点
- 节点会投票给优先级高的节点
多数票定义:
- 对于n个节点的复制集,多数票为(n/2)+1
- 3节点复制集:需要2票
- 5节点复制集:需要3票
选举优化
节点优先级:
- 通过priority参数调整节点优先级
- 优先级高的节点更容易成为主节点
- 范围:0-100
选举超时调整:
- 通过electionTimeoutMillis参数调整
- 较短的超时时间导致更快的故障转移
- 但可能导致不必要的选举
** heartbeat 配置**:
- 通过heartbeatIntervalMillis参数调整
- 默认2秒
- 较短的心跳间隔可以更快地检测到故障
复制集成员角色
核心角色
主节点(Primary):
- 唯一接受写操作的节点
- 维护oplog
- 向从节点发送心跳
- 参与选举
从节点(Secondary):
- 复制主节点数据
- 可以接受读操作(通过readPreference)
- 参与选举
- 向其他节点发送心跳
仲裁节点(Arbiter):
- 不存储数据
- 不参与数据复制
- 仅参与选举
- 节省资源
特殊角色
隐藏节点(Hidden):
- 不参与选举
- 不接受客户端请求
- 用于备份、报告等
- 通过hidden参数设置
延迟节点(Delayed):
- 故意落后于主节点
- 用于恢复误删除数据
- 通过slaveDelay参数设置
- 通常同时设置为隐藏节点
投票节点(Voting):
- 参与选举投票
- 通过votes参数设置
- 最多50个投票节点
- 建议投票节点数不超过7个
非投票节点(Non-Voting):
- 不参与选举投票
- 可以是从节点或隐藏节点
- 通过votes: 0设置
角色转换
主从转换:
- 自动故障转移时
- 手动执行rs.stepDown()时
- 主节点不可用时
角色配置:
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)
复制集读偏好
读偏好类型
primary:
- 仅从主节点读取(默认)
- 保证数据一致性
- 适用于对数据一致性要求高的场景
primaryPreferred:
- 优先从主节点读取
- 主节点不可用时从从节点读取
- 适用于大多数场景
secondary:
- 仅从从节点读取
- 提供读扩展
- 可能读取到过期数据
secondaryPreferred:
- 优先从从节点读取
- 从节点不可用时从主节点读取
- 适用于读密集型场景
nearest:
- 从网络延迟最低的节点读取
- 可以是主节点或从节点
- 适用于对延迟敏感的场景
读偏好配置
客户端配置:
javascript// Node.js 驱动示例 const client = new MongoClient(uri, { readPreference: 'secondaryPreferred', readConcern: { level: 'majority' } });连接字符串配置:
mongodb://host1:27017,host2:27017,host3:27017/?readPreference=secondaryPreferred集合级别配置:
javascript// 集合级别读偏好 db.collection.find().readPref('secondaryPreferred')
读关注(Read Concern)
读关注级别:
- local:读取节点上的最新数据
- available:读取可用数据
- majority:读取已被大多数节点确认的数据
- linearizable:线性izable读,确保读取到最新数据
- snapshot:读取特定时间点的数据
读关注与读偏好结合:
javascript// 读取已被大多数节点确认的从节点数据 db.collection.find().readPref('secondaryPreferred').readConcern({ level: 'majority' })
复制集监控
监控指标
复制延迟:
javascript// 查看复制延迟 rs.printSecondaryReplicationInfo()Oplog 状态:
javascript// 查看oplog状态 rs.printReplicationInfo()复制集状态:
javascript// 查看复制集状态 rs.status()节点状态:
javascript// 查看节点状态 rs.status().members.forEach(function(member) { print(member.name + ": " + member.stateStr); });
常见问题排查
复制延迟高:
- 检查网络连接
- 检查主节点负载
- 检查从节点负载
- 检查oplog大小
- 检查从节点是否有长时间运行的操作
选举失败:
- 检查节点健康状态
- 检查网络连接
- 检查多数节点是否可达
- 检查数据一致性
从节点不同步:
- 检查从节点状态
- 检查oplog是否被覆盖
- 考虑重新初始化从节点
- 检查防火墙设置
最佳实践
架构设计
节点数量:
- 建议使用奇数节点(3、5、7)
- 避免使用偶数节点(除非有仲裁节点)
- 最多50个节点
分布部署:
- 跨可用区部署
- 考虑网络延迟
- 避免单点故障
硬件配置:
- 所有节点使用相同的硬件配置
- 确保足够的存储空间
- 考虑使用SSD存储
配置优化
Oplog 大小:
- 建议能存储24-48小时的操作
- 根据写入负载调整
- 定期监控oplog窗口
选举配置:
- 根据业务需求调整electionTimeoutMillis
- 合理设置节点优先级
- 考虑使用heartbeatIntervalMillis
读偏好配置:
- 根据业务需求选择合适的读偏好
- 考虑结合readConcern使用
- 监控读操作分布
日常维护
定期检查:
- 检查复制集状态
- 检查复制延迟
- 检查oplog状态
- 检查节点健康状态
备份策略:
- 定期备份数据
- 考虑使用延迟节点
- 测试恢复流程
升级策略:
- 使用滚动升级
- 先升级从节点,再升级主节点
- 监控升级过程
常见问题(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:
