rs.stepDown()执行前须确保多数派节点可达且有Primary,参数需合理设置catchUpSecs以避免回滚,应用层必须处理NotMaster错误并启用自动重试。

rs.stepDown() 执行前必须确认多数派可达
执行 rs.stepDown() 不是“点一下就完事”,它依赖副本集能立刻完成一次成功选举。如果当前只有 2 个节点在线(比如 1 Primary + 1 Secondary),而第三个 Secondary 网络不通或宕机,那么 rs.stepDown() 会卡住,副本集进入无 Primary 状态,所有写操作返回 NotMaster 错误。
实际检查方法:
- 运行
rs.status().members,确认至少有STARTUP2或SECONDARY状态的节点 ≥ 2 个(对三节点集即全部在线;对五节点集需 ≥ 3 个在线且可投票) - 检查
rs.status().ok为 1,且rs.status().hasPrimary为 true(降级前必须有 Primary) - 特别注意仲裁节点(arbiter):它不复制数据但参与投票,
votes总和必须 ≥ 3 且多数在线,否则选举无法达成法定票数
rs.stepDown() 参数设置不当会导致窗口期失控
默认 60 秒禁选期看似安全,但可能过长或过短——关键不是“设多少”,而是“是否匹配你的恢复节奏”。传参 rs.stepDown(30) 缩短时间没问题,但设成 rs.stepDown(5) 极易失败:新 Primary 还没完全接任,原节点就重新参选,可能引发反复切换。
更危险的是漏掉第二个参数 catchUpSecs:
-
rs.stepDown(60, 10)表示:先等最多 10 秒让所有 Secondary 同步完最新 oplog,再强制降级;若超时未同步完,命令直接失败,不降级 - 省略
catchUpSecs(即只写rs.stepDown(60))等于跳过同步等待,可能让新 Primary 落后大量写入,后续触发回滚 - 生产环境建议显式指定,如
rs.stepDown(60, 30),给追赶留出余量
rs.stepDown() 后应用层必须处理 NotMaster 错误
降级瞬间,原 Primary 断开所有写连接,客户端收到 NotMaster 或 InterruptedAtShutdown 错误。这不是异常,是预期行为。但很多应用没做重试或路由切换,直接报错中断。
实操要点:
- 驱动层需启用自动重试(如 PyMongo 的
retryWrites=True,Java Driver 的retryWrites=true) - 连接字符串中避免硬编码 Primary 地址,必须用副本集名 + 多个 seed host,例如
mongodb://node1:27017,node2:27017,node3:27017/?replicaSet=myrs - 监控指标应包含
lastWriteDate和primary字段变化,而不是只盯rs.isMaster().ismaster
别混淆 rs.stepDown() 和配置类操作
rs.stepDown() 是运行时行为控制,和改 priority、freeze() 完全不同层级。试图用 rs.freeze() 在 Primary 上执行会直接报错 freeze can only be run on secondary nodes;而改 priority 必须配 rs.reconfig(),且该操作本身不触发选举——你改完 priority=10,当前 Primary 还在岗,除非你再补一句 rs.stepDown()。
真正要换主,顺序只能是:
- 先确保目标 Secondary 同步延迟 rs.status().members[n].optimeDate 差值)
- 再调高它的
priority并rs.reconfig()(记得递增version) - 最后在现 Primary 上执行
rs.stepDown(60, 30)
中间任何一步跳过或颠倒,都可能让集群卡住或选错节点。最常被忽略的是:降级后不验证新 Primary 是否真的接管了写流量——得查 rs.isMaster().primary 和 db.serverStatus().connections.current 变化,不能只信 shell 提示。


















