WriteConflict错误112是MongoDB因MVCC快照校验失败强制中止事务的现象,非业务冲突;它由Snapshot Isolation机制触发,当事务依赖的文档快照被其他事务修改时立即抛出UncategorizedMongoDbException并含"WriteConflict"字符串,需精准匹配该关键字实施指数退避重试。

WriteConflict 错误 112 是什么现象?
执行事务时突然抛出 WriteConflict(错误码 112),日志里可能还夹着 TransientTransactionError 或干脆没有标签;事务被中止,但业务没报错、数据也没写进去,重试后又成功——这是最典型的症状。它不是业务逻辑冲突,而是 WiredTiger 存储引擎在底层检测到多个事务试图修改同一文档(或同一页面内多个文档)的内存快照版本不一致,强制终止其中一个。
为什么分片集群里 WriteConflict 更频繁?
分片环境下,事务协调器(mongos)需向多个分片发送两阶段提交请求,各分片本地事务的生命周期不同步:某个分片已提交,另一个还在预写日志阶段,此时若外部写入命中同一文档,就极易触发快照版本冲突。尤其当:
- 多个事务高频更新相同分片上的热点文档(如用户中心、库存主键)
- 事务跨分片且涉及大量小批量写(比如每批 10 条,但并发开 50 个 session)
- 分片键设计不合理,导致写请求集中打到少数 chunk 上
WriteConflict 不是偶发,而是常态。
怎么快速定位冲突源头?
别只看应用层日志。直接查 MongoDB 内置指标:
- 运行
db.serverStatus().metrics.transaction.writeConflicts,确认是否持续增长 - 开启慢事务日志:
setParameter: { logTransactionStats: true },再复现问题,搜"writeConflicts": 1的条目,里面会带ns(集合名)、keys(冲突文档的 _id 或索引键) - 用
db.currentOp({ "secs_running": { "$gt": 2 } })抓长事务,重点看active和waitingForLock字段
orders 集合的 { userId: 1, status: 1 } 索引上,那基本就是批量状态更新没控制好并发粒度。
WriteConflict 发生后该不该重试?怎么重试才安全?
必须重试,但不能无脑重试:
- 显式检查
error.code === 112或error.hasErrorLabel("TransientTransactionError")(注意:4.2+ 版本中WriteConflict不自动带该标签) - 外层控制重试,不要在
session.withTransaction()回调里自己循环;推荐指数退避,比如[100, 300, 900]毫秒 - 每次重试前调用
session.abortTransaction(),否则下次startTransaction()会失败 - 对幂等性没保障的操作(如扣款),重试前要加业务层防重,比如用
transactionId做去重表


















