4. 副本集成员配置详解

副本集不是“把几台机器加进去就完事”。同一个 rs.add() 背后,成员属性决定了谁更容易成为主节点、谁参与选举、谁对客户端不可见、谁可以落后主节点一段时间、谁不建索引。把这些属性理解成“成员的角色开关”,才能把读写分离、报表节点、灾备节点落到可执行的配置上。

一、五个核心属性分别控制什么

priority(优先级)决定选举倾向。取值 0~1000,默认 1。数值越大,在数据同样新的候选者中越容易被选为主节点;priority 为 0 的成员永远不会主动竞选主节点,但可以成为候选投票者。它不改变数据新旧判断,只在“数据同样新”的候选集合里起作用。

votes(投票权)决定该成员是否参与选举投票,取值 0 或 1,默认 1。一个副本集最多 7 个有投票权的成员。votes=0 的成员不参与投票,但可以复制数据、可以被读。注意:votes 与 priority 相互独立,priority=0 且 votes=1 是常见组合,用于“只读但可投票”的成员。

hidden(隐藏)为 true 时,该成员对客户端驱动不可见,不会出现在 db.hello() 返回的可见成员列表中,因此不会被普通读请求选中。隐藏成员必须 priority=0,且通常保留 votes=1。它适合做报表、备份、离线分析等“只被内部读取”的节点。

slaveDelay(延迟)让该成员比主节点滞后指定秒数应用 oplog,用于抵御误删、误更新。它必须配合 priority=0 和 hidden=true 使用,否则延迟节点可能被选为主节点,把旧数据“扶正”。延迟时间应大于预估的误操作发现与恢复窗口,常见为 3600 秒(1 小时)。

buildIndexes(建索引)为 false 时,该成员不构建除 _id 外的索引,只做数据复制。它适合纯灾备、冷备节点,可显著降低写入放大和磁盘占用。代价是该成员不能参与正常读查询(缺索引会拖慢),且一旦设为 false,后续改回 true 需要重新同步。

记忆要点:priority 管“谁当主”,votes 管“谁投票”,hidden 管“谁对客户端不可见”,slaveDelay 管“谁故意落后”,buildIndexes 管“谁不建索引”。

副本集成员属性与角色映射 同一份数据,不同属性组合 → 不同职责 主节点 Primary priority: 10 votes: 1 hidden: false 从节点 Secondary priority: 5 votes: 1 hidden: false 隐藏报表节点 priority: 0 votes: 1 hidden: true 延迟灾备节点 priority: 0 slaveDelay: 3600 hidden: true oplog 复制方向 → 属性取值与约束 priority 0 ~ 1000 默认 1 votes 0 或 1 最多 7 票 hidden true / false 需 priority=0 slaveDelay 秒数 需 hidden=true buildIndexes true / false 默认 true 约束链:hidden=true → priority=0;slaveDelay>0 → hidden=true 且 priority=0;buildIndexes=false → 不可参与常规读。 选举规则:只有 votes=1 的成员投票;候选者中数据最新者优先,数据同样新时 priority 高者优先。 读写分离:普通读走可见 Secondary;报表读走 hidden 节点;灾备读走 slaveDelay 节点。

二、读写分离:让读请求落到正确的节点

读写分离的目标是把读流量从主节点分流,同时保证读到的数据可接受。MongoDB 驱动通过 readPreference 控制读路径:primary 只读主,primaryPreferred 优先主、主不可用读从,secondary 只读从,secondaryPreferred 优先从,nearest 选网络最近。要让某个从节点参与普通读,它必须对客户端可见,即 hidden=false。

典型配置:主节点 priority=10、votes=1;两个可读从节点 priority=5、votes=1、hidden=false;一个隐藏报表节点 priority=0、votes=1、hidden=true。应用连接串使用 readPreference=secondaryPreferred,普通查询会落到两个可读从节点;报表任务单独用直连串连到隐藏节点,避免干扰在线流量。

// 添加可读从节点
rs.add({ host: "mongo2:27017", priority: 5, votes: 1, hidden: false })

// 添加隐藏报表节点
rs.add({ host: "mongo-report:27017", priority: 0, votes: 1, hidden: true })

// 添加延迟灾备节点
rs.add({
  host: "mongo-dr:27017",
  priority: 0,
  votes: 1,
  hidden: true,
  slaveDelay: 3600,
  buildIndexes: true
})

三、报表节点:隐藏但不缺席投票

报表节点通常承担聚合、导出、BI 查询,负载重且不希望被应用驱动自动选中。配置为 hidden=true、priority=0、votes=1 后,它不会出现在普通驱动的可见成员列表里,但仍在选举中投票,保证副本集投票成员数稳定。报表程序使用直连方式连接该节点,例如 mongodb://mongo-report:27017/report?directConnection=true,并显式设置 readPreference=secondary。

需要注意:隐藏节点仍然会应用 oplog,只是对客户端不可见。如果报表查询很重,建议给它单独的资源配额,并监控其复制延迟。若延迟持续增大,说明该节点已跟不上主节点写入,需要扩容或降低报表并发。

四、灾备节点:延迟 + 不建索引

灾备节点的核心价值是“误操作保护”。假设运维误删了一张表,主节点和普通从节点会立即同步删除,而 slaveDelay=3600 的节点会在一小时内仍保留删除前的数据。发现误操作后,可以停掉该节点的复制、从它导出数据,再恢复到主节点。配置上必须 priority=0、hidden=true、slaveDelay=3600,votes 可保留 1。

如果灾备节点只用于冷备、不参与查询,可以设置 buildIndexes=false,减少索引维护开销和磁盘占用。但要注意:buildIndexes=false 的成员不能用于常规读,且改回 true 需要重新同步。因此建议在初始添加时就确定好,不要中途反复切换。

slaveDelay 不是备份替代品。它只能抵御“在延迟窗口内被发现”的误操作;如果误操作超过延迟时间才被发现,延迟节点也会同步。关键数据仍需配合 mongodump 或文件系统快照。

五、配置后的验证与观察

配置完成后,用 rs.status() 查看每个成员的 stateStr、health、optimeDate 和 syncSourceHost。重点确认:隐藏节点是否显示为 SECONDARY 且 hidden 为 true;延迟节点的 optimeDate 是否比主节点落后约 3600 秒;votes 总数是否不超过 7。再用 db.hello() 从应用侧确认可见成员列表,确保隐藏节点没有出现在普通驱动的候选列表中。

角色priorityvoteshiddenslaveDelaybuildIndexes
主节点101false0true
可读从节点51false0true
隐藏报表节点01true0true
延迟灾备节点01true3600false

最后提醒一个常见误区:不要为了“多几个投票者”而随意增加 votes=1 的成员。副本集最多 7 个投票成员,超过后需要把部分成员设为 votes=0。投票成员越多,选举达成多数派的门槛越高,跨机房部署时尤其要计算好多数派所在位置,避免网络分区导致无法选出主节点。