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 管“谁不建索引”。
二、读写分离:让读请求落到正确的节点
读写分离的目标是把读流量从主节点分流,同时保证读到的数据可接受。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() 从应用侧确认可见成员列表,确保隐藏节点没有出现在普通驱动的候选列表中。
| 角色 | priority | votes | hidden | slaveDelay | buildIndexes |
|---|---|---|---|---|---|
| 主节点 | 10 | 1 | false | 0 | true |
| 可读从节点 | 5 | 1 | false | 0 | true |
| 隐藏报表节点 | 0 | 1 | true | 0 | true |
| 延迟灾备节点 | 0 | 1 | true | 3600 | false |
最后提醒一个常见误区:不要为了“多几个投票者”而随意增加 votes=1 的成员。副本集最多 7 个投票成员,超过后需要把部分成员设为 votes=0。投票成员越多,选举达成多数派的门槛越高,跨机房部署时尤其要计算好多数派所在位置,避免网络分区导致无法选出主节点。