writeconflict不是代码错误,而是wiredtiger引擎因内存页争用主动中止事务的并发控制信号;根本原因是长事务+大批量写入导致缓存不足(默认50%物理内存),触发快照与日志管理冲突,需拆分事务、调大wiredtigercachesizegb并避免单事务百万级操作。

WriteConflict 不是代码写错了,而是 WiredTiger 引擎在事务执行中检测到写冲突后主动中止事务的协作机制——它本质是并发控制信号,不是失败。
WriteConflict 真正触发点在存储引擎层
它常被误认为是业务逻辑冲突,实际根源是 WiredTiger 的内存页管理压力:事务生命周期越长、修改文档越多,引擎需维护的快照、预写日志和检查点缓存就越大。默认 wiredTigerCacheSizeGB(物理内存 50%,最低 256MB)在批量插入 80 万+ 文档时极易不足,导致页面争用加剧,最终触发 WriteConflict 并中止事务。
- 不是“两个事务改了同一行”,而是“多个事务同时刷脏页、更新快照时抢同一块内存页”
- 事务内执行
insertMany、updateMany或连续findOneAndUpdate都会快速耗尽缓存 - 分片集群下各 shard 独立缓存,但冲突仍会高频出现,因每个分片都面临相同压力
事务粒度太大是最常见诱因
把百万级写入塞进单个事务,远超 MongoDB 推荐的事务边界:生命周期应
-
i == sampleDataList.size()这类错误边界判断会导致最后一组数据未提交,还可能因clear()后重复添加造成数据丢失 - 事务里混用读操作(如
find)和写操作,尤其带sort或无索引查询,会拉长事务执行时间,放大冲突窗口 - 没设
readConcern: "snapshot"的事务,在高并发下读取可能跨快照,间接增加写冲突机会
并发资源争抢顺序不一致会雪上加霜
MongoDB 不强制事务内操作顺序,如果不同事务对同一组文档按不同 _id 顺序修改(比如 A 先改 _id: 100 再改 _id: 200,B 反过来),就会形成隐式锁依赖链。WiredTiger 不构建全局等待图,无法检测或打破这种循环,只能靠 WriteConflict 中止其中一个。
- 必须显式对操作对象按
_id或业务唯一键升序/降序排列,再依次执行updateOne或replaceOne - 不要依赖
sort参数“自动排队”——它只影响选哪个文档,不改变锁获取顺序 - 在分片集群中,跨分片事务更要严格统一排序逻辑,否则各 shard 加锁节奏错位,冲突概率翻倍
真正难处理的不是 WriteConflict 本身,而是它背后暴露的设计惯性:把关系型数据库的事务习惯直接平移过来,却忽略了 WiredTiger 的 MVCC 和文档级锁机制对操作节奏的硬性约束。调大缓存或加重试只是止痛,重构事务边界和资源访问顺序才是根治点。











