升级后副本集卡在STARTUP2或反复选举失败,根本原因是旧版配置被新版本拒绝解析,必须清空local.system.replset并用rs.initiate()重建配置。

升级后副本集卡在 STARTUP2 或反复选举失败、始终没有 PRIMARY,大概率是配置兼容性断裂或节点身份混乱导致的——不是网络或资源问题,而是 MongoDB 拒绝承认自己属于同一个副本集。
为什么 rs.status() 显示所有节点都在 STARTUP2 或 RECOVERING?
升级(尤其是跨大版本,如 4.4 → 6.0 或 5.0 → 7.0)后,local.system.replset 中存储的旧版配置可能被新版本内核拒绝解析。节点启动时读取该集合,发现字段缺失(如 protocolVersion)、值非法(如 priority 为 float 而非 int),或签名不匹配,就会停在 STARTUP2 状态,拒绝加入任何拓扑。
常见错误日志:"replSetInitiate failed: InvalidOptions: priority must be an integer"、"could not find configuration entry for self"、"Node is not in the config"。
- 不要尝试
rs.reconfig()—— 节点连rs.conf()都拿不到有效配置,根本无法构造新 cfg - 不要删整个
local库 —— 会丢失oplog.rs和startup_log,导致同步起点错乱 - 升级前未关闭 journal 或未启用
writeConcernMajorityJournalDefault的集群,升级后更容易因回滚逻辑变更而卡住
必须清空 local.system.replset 并用 rs.initiate() 重建
这是唯一能绕过配置校验僵局的路径。新版本要求配置“从零开始”被接受,而非热更新。
操作前确认:所有节点已停机;备份了 data 目录(尤其 local 子目录);知道原 _id 名称和各成员 host 地址(不能是 localhost)。
- 逐个启动节点,但加
--setParameter enableTestCommands=1(部分版本需要) - 连上任一节点的
mongoshell,执行:use local→db.system.replset.drop() - 再执行:
db.oplog.rs.drop()(强制重置复制起点,避免 optime 冲突) - 然后立即运行完整
rs.initiate(),例如:rs.initiate({_id:"rs0", members:[{_id:0,host:"node1:27017"},{_id:1,host:"node2:27017"},{_id:2,host:"node3:27017"}]}) - 注意:
_id必须是整数;host必须可被其他节点 DNS 解析;首次rs.initiate()不支持hidden、priority等字段,这些得等 PRIMARY 出现后再rs.reconfig()
升级后验证 writeConcernMajorityJournalDefault 是否生效
很多团队修复选主后立刻上线,结果几分钟后出现静默数据丢失——根源是升级默认关闭了 writeConcernMajorityJournalDefault,而旧版配置里没显式声明它。
即使节点成功成为 PRIMARY,若该参数为 false,w: "majority" 写入在 majority 节点确认后仍可能因崩溃丢失(journal 未强制落盘)。
- 在 PRIMARY 上运行:
db.adminCommand({ getParameter: 1, writeConcernMajorityJournalDefault: 1 }) - 如果返回
"writeConcernMajorityJournalDefault" : false,必须修改每个节点的启动参数,添加:--setParameter writeConcernMajorityJournalDefault=true,然后重启 - 仅靠
rs.reconfig()补这个参数无效,它属于全局 server 参数,不在副本集配置中
真正麻烦的不是选不出主,而是选出来之后写入不可靠——journal 默认开关变化、oplog 格式升级、配置字段语义收紧,这些细节在升级文档里藏得深,但直接决定数据是否安全。


















