timeoutms设为1500毫秒更合理,因选举通常1–3秒完成,1500ms可快速抛出notwritableprimary等错误并交由业务重试,避免30秒超时拖垮响应;需配合sleep(200+jitter)和ismaster探测主节点,且写操作必须显式配置w:"majority"防脑裂。

NotWritablePrimary 错误说明当前没有可写主节点,不是连接问题,而是副本集正处于选举中或主节点不可用——必须快速失败重试,不能靠延长超时硬扛。
为什么timeoutMS设成1500毫秒比设成30秒更合理
选举通常在1–3秒内完成,但驱动默认30秒超时会让请求卡满全程,拖垮下游响应。设timeoutMS=1500能确保在多数选举窗口内快速抛出NotWritablePrimary或InterruptedDueToReplStateChange,把控制权交还业务层。
- 该参数控制整个操作生命周期(选节点+建连接+发命令+等响应),不是单纯“等待主节点出现”
- Java驱动会抛
MongoTimeoutException,Go驱动是ContextDeadlineExceeded,Node.js则触发.MongoServerSelectionError,需按实际错误类型做分支处理 - retryWrites=true 对这类错误无效——选举期间所有写都不可重试,只有连上新主后的后续请求才可能重试
重试前必须sleep + 主节点探测,不能无脑循环
立刻重试大概率再次打到无主状态,反而加剧连接池压力。正确做法是先短暂停顿,再轻量探测是否已有可用主节点。
- sleep建议用
200 + jitter(比如±50ms随机偏移),避免多个实例同步重试造成雪崩 - 探测用
db.runCommand({isMaster: 1}),检查返回里ismaster为true且hosts数组长度匹配预期(如3节点集群应返回3个host) - 别信
rs.status()——它只反映本节点视角;isMaster命令由驱动直连当前节点执行,结果更实时 - 如果探测返回
ismaster:false或hosts数量异常,继续sleep再探,最多试3轮就该降级或告警
写操作必须显式指定w: "majority",否则可能静默写入假主
默认w: 1在脑裂时会写入少数派“伪PRIMARY”,导致数据分裂或回滚——这不是配置遗漏,而是设计陷阱。
- 哪怕只配了
w: "majority",也得确认writeConcernMajorityJournalDefault为true,否则没开journal的节点计入majority后崩溃即丢数据 - 查全局设置:运行
db.adminCommand({ getParameter: 1, writeConcernMajorityJournalDefault: 1 }) - 查单节点journal状态:
db.runCommand({ getCmdLineOpts: 1 })里看storage.journal.enabled是否为true - 应用层所有写操作(
insertOne、updateMany等)都应显式传{ writeConcern: { w: "majority" } },不能依赖默认
真正麻烦的不是报错本身,而是错误背后可能藏着脑裂——看到NotWritablePrimary的同时若日志里夹杂NotMaster或Connection refused,立刻查rs.status()有没有两个"stateStr": "PRIMARY",别等自动恢复。











