rs.reconfig() 修改 votes 必然触发选举并中断写入服务10–20秒;votes=0 节点 priority 必须为 0;缩容应先降权(votes=0、priority=0、hidden=true)再观察24小时后 rs.remove();投票节点数须为奇数且≤7,避免脑裂。

rs.reconfig() 修改 votes 会强制触发选举
直接改 votes 字段必须走 rs.reconfig(),而这个操作会让当前 PRIMARY 主动下台——不是“可能”,是必然。mongod 会立刻关闭所有客户端连接,整个写入服务中断,通常持续 10–20 秒。这不是 bug,是 MongoDB 的设计约束:投票权变更属于元数据强一致性操作,必须通过一次完整选举来确认新配置生效。
常见错误现象:rs.reconfig() 执行后,rs.status().members 显示多个节点状态为 STARTUP2 或 RECOVERING,但写请求持续失败;或应用报错 NotPrimaryNoSecondaryOk,说明客户端还没完成重连和拓扑刷新。
- 必须在业务低峰期执行,不能在流量高峰或事务密集窗口做
- 确保驱动版本支持自动重试(如 Python PyMongo ≥ 4.0、Java Driver ≥ 4.7),否则需手动捕获
NotPrimary异常并重试 - 不要在混合版本副本集中操作(比如 v6.0 和 v7.0 节点共存),不同版本对
votes合法性校验逻辑可能不一致,容易卡在 reconfig 验证阶段
votes=0 的节点 priority 必须设为 0
MongoDB 强制要求:任何 votes: 0 的成员,其 priority 值只能是 0。如果试图把一个已有数据的 secondary 设置为 votes: 0 但保留 priority: 1,rs.reconfig() 会直接报错 Cannot set priority > 0 for a node with votes: 0。
所以缩容前若想保留该节点作为只读副本(比如用于备份或报表),得同步清零优先级:
cfg = rs.conf()
cfg.members[2].votes = 0
cfg.members[2].priority = 0
rs.reconfig(cfg, {force: true})
- 这个
force: true不是可选的“绕过检查”,而是告诉 MongoDB:我清楚这会触发选举,请执行 - 如果该节点正在承担 readPreference=secondary 的读流量,改完后它将不再参与选举,也不会被选为主,但数据仍可读(只要没被
rs.remove()) - 别漏掉
hidden: true—— 如果你不想让应用偶然路由到它,顺手加上更稳妥
缩容 ≠ 立刻 rs.remove(),先降权再观察
真实运维中,最常踩的坑是:加节点时用 rs.add() 很快,删节点就急着 rs.remove()。但 rs.remove() 是硬删除,一旦执行,该节点立刻从配置中消失,如果此时它还是 SECONDARY 并且正被应用读取,连接会瞬间断开;更糟的是,如果它恰巧持有最新 oplog,其他节点还没追平,删掉可能导致部分写入丢失(尤其当 write concern 不是 w:majority 时)。
推荐分两步走:
- 第一步:用
rs.reconfig()把目标节点votes设为 0、priority设为 0、hidden设为 true,让它彻底退出选举和读路由体系,但保留数据同步(initial sync 已完成) - 第二步:观察 24 小时以上,确认无应用报错、oplog 追赶延迟归零、监控里该节点状态稳定为
SECONDARY且optimeDate与主节点一致,再执行rs.remove("host:port")
注意:rs.remove() 后,该节点上的 mongod 进程不会自动关,得人工停掉或回收机器资源;它也不会自动从 DNS 或负载均衡列表里剔除,这些要额外处理。
投票节点总数不能超过 7,缩容后记得检查 majority
副本集最多只允许 7 个 votes: 1 的节点。如果你原本有 7 个投票节点,缩掉一个后变成 6,看起来只是少一个,但多数派门槛从 ⌈7/2⌉ = 4 变成了 ⌈6/2⌉ = 3,容错能力反而下降了:原来能扛 3 台挂,现在只能扛 2 台。更关键的是,6 是偶数,网络分区时极易出现脑裂(split-brain)——两边各 3 个节点,都认为自己拥有多数,各自选出主节点。
所以缩容后务必检查:
- 运行
rs.status().majorityVoteCount,确认值是奇数且符合预期 - 如果缩完只剩 6 个投票节点,要么补一个仲裁节点(
rs.addArbiter()),要么把某个votes: 0的节点重新设回votes: 1(前提是它 priority > 0 且未超 7 限制) - 用
rs.status().members.filter(m => m.votes === 1)数一下当前真正在投票的节点数,别只看rs.conf().members.length
缩容的本质不是删机器,而是重新平衡投票权、容错能力和读写路径——参数调错一个,可能让高可用变成单点依赖。











