跨机房副本集选举失败的根本原因是votes配置与网络可达性不匹配,导致无法形成有效多数派或引发脑裂;priority仅在全连通时影响选主倾向,断网后失效;tags用于读写调度但需配合readPreferenceTags和完整rs.reconfig()生效。

跨机房副本集选举失败的典型现象
节点失联后主节点没退选、从节点不升主、整个副本集卡在 SECONDARY 或 RECOVERING 状态,甚至出现两个 PRIMARY ——这不是 MongoDB 坏了,而是投票权(votes)和网络可达性没对齐。
根本原因在于:MongoDB 选举要求「获得多数票」,而多数是基于 members[n].votes 总和计算的,不是节点总数。跨机房时若所有节点都设 votes: 1,但其中两个机房之间网络中断,剩余节点数可能仍 ≥ ⌈total/2⌉,导致分裂脑(split-brain)。
- 必须确保每个机房至少有 1 个
votes: 1节点,且任意单机房断网后,其余机房的votes总和 严格小于 多数阈值 -
priority不影响选举资格,只在所有节点可达时影响“谁更可能当选”;断网后它完全失效 - 隐藏节点(
hidden: true)默认votes: 0,即使显式设priority: 5也无效,必须同时加votes: 1
如何用 tags + readPreference 实现就近读写
打标签本身不改变选举逻辑,但它是应用层流量调度的唯一依据。MongoDB 不识别“上海机房”或“华北”,只认你定义的 key-value,比如 "dc": "shanghai"。
常见失效场景:readPreference=nearest 配置了,但所有节点 tags 字段为空 → 驱动退回到按 ping 延迟选节点,地理感知完全没生效。
- 统一约定 tag key 名,推荐用
"dc"(data center)或"region",避免混用"location"/"zone"导致匹配失败 - 连接字符串中必须显式带
readPreferenceTags,例如:mongodb://host1,host2/?readPreference=primaryPreferred&readPreferenceTags=dc:shanghai - 写关注(
writeConcern)设为{"w": "majority"}时,若多数节点跨机房,一次写可能等 5 秒以上甚至超时;应结合业务容忍度改用{"w": 2}或带dc标签的自定义 write concern
rs.reconfig() 修改 priority 或 tags 后不生效
改完配置不重启服务、不调 rs.reconfig(),变更就是白改。MongoDB 不监听配置字段的局部更新,只认完整新配置对象。
错误做法:只传 { members: [{ _id: 0, priority: 10 }] } → 报错 replSetReconfig invalid config 或静默忽略。
- 必须用
rs.conf()拿到当前完整配置,修改目标字段后,再传给rs.reconfig() - 修改
tags时,members[n]中的_id、host、priority、votes等字段一个都不能少,否则会被清空或重置为默认值 - 生产环境建议先在测试副本集上跑通
rs.reconfig()流程,再同步到线上;配置错误可能导致整个副本集不可写
为什么不能把 config server 跨机房部署
分片集群里,config server 是元数据的单点权威。它必须是 3 节点副本集,但绝不能横跨双活机房——哪怕只延迟 50ms,也会让 mongos 拿到过期的 chunk 分布信息,引发写入路由错误、查询丢数据、moveChunk 卡死。
真实故障表现:sh.status() 显示 stale config version,日志里反复刷 waiting for majority commit。
- config server 必须集中部署在同一机房(推荐主中心),mongos 可分散部署,但必须连这个单中心 config server
- 不要试图用「异地多活」思路套 config server;它的设计目标是强一致,不是高可用扩展
- 如果真需要跨机房容灾,方案只能是:主中心全量运行 config server + 备中心冷备整套集群,靠人工或脚本触发 failover,而非自动切换
跨机房高可用真正的复杂点不在配置项本身,而在对「多数派」和「网络分区」边界的清醒认知——所有 priority、tags、writeConcern 的意义,都建立在你明确知道「此刻哪些节点能互相通信」的基础上。一旦假设崩塌,再精细的配置也会失效。











