retryWrites=true 仅在 MongoDB 4.0+ 服务端与兼容驱动下对单文档写操作(如 updateOne)生效,不适用于批量操作、事务或非幂等写入;重试最多1次,且需操作幂等、连接字符串正确配置。

为什么加了 retryWrites=true 还不重试?
根本原因:这个参数只在 MongoDB 4.0+ 服务端 + 兼容驱动(如 Node.js 4.0+、Python PyMongo 3.12+)下才生效,且必须满足「单文档写操作」前提。批量写(insertMany、updateMany)、事务内写、带 writeConcern: { w: "majority" } 但多数节点不可用时,都不会触发自动重试。
常见错误现象:WriteConcernError 或 NotPrimaryError 直接抛出,没看到重试日志;或者写了但数据没出现,误以为“重试失败”——其实是重试成功了,但你没检查返回值或没等确认。
- 确保连接字符串里明确带上
?retryWrites=true(不是retrywrites或大小写混用) - Node.js 驱动中,如果用
MongoClient.connect(url, { retryWrites: true }),它会覆盖 URL 中的设置,注意不要重复/冲突 - PyMongo 3.12+ 默认开启
retryWrites,但如果你手动传了retryWrites=False或用了旧版连接方式(如mongoclient = MongoClient(...)不带参数),就等于关掉了
retryWrites 能重试哪些操作?
只对满足「单文档语义」的写命令生效:包括 insertOne、replaceOne、updateOne、deleteOne 和 findOneAndUpdate(含 upsert: true)。它不会重试 findAndModify 原始命令(已废弃),也不重试任何涉及多文档、跨集合或带 arrayFilters 的复杂更新。
典型使用场景:副本集主节点临时故障切换期间,客户端发出去的 updateOne 被路由到原主(现在是 secondary),驱动捕获 NotPrimaryError 后自动重试到新主节点——前提是你的操作没副作用(比如自增字段、时间戳生成逻辑不能放在应用层做)。
- 重试最多 1 次,不循环;超时由
serverSelectionTimeoutMS控制,默认 30 秒 - 重试时会重新生成
operationId,服务端靠lsid+txnNumber(即使非事务)识别幂等性,所以务必保证写操作本身是幂等的 - 如果写操作包含客户端生成的
_id,重试才安全;用ObjectId()自动生成的也 OK,但别依赖Date.now()这类非确定值作为关键字段
和 w: "majority" 写关注混用时要注意什么?
两者不冲突,但行为叠加后容易误解:开启 retryWrites=true 后,一次 updateOne 可能先因 NotPrimaryError 重试,重试成功后又因写关注未满足(比如 majority 节点数不足)而报 WriteConcernError ——这时不会再次重试,因为写操作本身已完成,只是确认没拿到足够应答。
性能影响:重试本身不增加延迟毛刺,但会让平均写耗时略升(尤其网络抖动时);兼容性上,3.x 驱动完全忽略该参数,4.0+ 服务端若收到带 txnNumber 的非事务请求会静默丢弃重试逻辑。
- 测试时别只看是否报错,用
mongosh连 secondary 手动rs.stepDown()触发主切,再观察客户端日志里有没有Retrying write due to NotPrimaryError - 生产环境建议搭配
maxPoolSize(如 10)和minPoolSize(如 5),避免重试时连接池卡住 - Java 驱动需显式启用
retryWrites=true,它不像 PyMongo 那样默认开;Go 的mgo库根本不支持,得换mongo-go-driver
怎么验证重试机制真在工作?
最可靠的方式不是看代码有没有报错,而是抓包或开服务端日志:在 mongod 启动时加 --logLevel 1 --setParameter traceWriteConcern=1,然后触发一次主节点切换,搜索日志里是否出现重复的 op: update ns: db.col 记录(带不同 connectionId 但相同 lsid)。
应用层简单验证:用 mongosh 连到 primary,执行 db.runCommand({configureFailPoint: "failCommand", mode: {times: 1}, data: {failCommands: ["update"], closeConnection: true}}),再跑一次 updateOne,看客户端是否返回结果而非连接异常。
- 别依赖
driverInfo或连接字符串里的注释来判断是否启用,实际以驱动版本 + 服务端能力 + 操作类型三者交集为准 - 监控时重点看
mongodb.driver.write_retries_total(Prometheus)或日志关键词,而不是业务成功率——后者可能掩盖重试成功但业务逻辑出错的问题 - 真正复杂的点在于:重试只解决“网络/主从切换”类瞬时故障,对持久性错误(如唯一键冲突、磁盘满)完全不干预,这类问题还得靠应用层兜底

















