MongoDB 4.0+ 事务不额外分配内存,但强制保留修改文档的全量旧版本快照,导致 WiredTiger cache 中 bytes currently in the cache 和 dirty 累计值激增,易触发 OOM;需监控 %used > 95%、%dirty > 20% 及 currentActive 指标组合预警,并通过压事务粒度、拆批、调优 eviction 线程等手段干预。

MongoDB 4.0+ 的多文档事务本身不“额外分配”内存,但会强制 WiredTiger 在内存中保留事务内所有修改文档的**完整旧版本快照**——只要事务没提交或回滚,这些副本就一直卡在 cache 里,直接推高 bytes currently in the cache 和 bytes dirty in the cache cumulative。这不是泄漏,是设计使然;但一旦控制不住,就会触发 OOM。
事务快照如何把 WiredTiger cache 塞满
WiredTiger 的事务快照不是指“差异”,而是每个被修改文档的**全量原始副本**。比如一个事务里 updateOne 了 1000 条记录,每条平均 2MB,那光快照就占 2GB 内存,还不算索引更新、日志缓冲等开销。
- 事务内单文档含大字段(如
base64图片、日志块)时,快照大小 = 文档原始大小 × 修改次数,极易突破wiredTigerCacheSizeGB限制 -
readConcern: "snapshot"或writeConcern: {w: "majority"}会延长快照保留时间,进一步堆积 - 事务未及时提交/回滚(比如卡在慢查询、网络等待),快照持续驻留,
currentActive指标会上升,但currentInactive不降——这是快照正在膨胀的明确信号
监控 %dirty 和 %used 的真实含义
mongostat 输出里的 %dirty 和 %used 是 WiredTiger cache 的实时健康水位,不是 mongod 进程整体内存使用率。它们直接反映事务压力:
-
%used > 95%:cache 几乎打满,WiredTiger 开始主动淘汰 clean page,用户请求延迟上升 -
%dirty > 20%:脏页比例过高,用户线程会被拉去参与刷盘(eviction),出现明显阻塞 - 这两个值同时飙高,且
db.serverStatus().metrics.transaction.currentActive > 3,基本可判定是事务导致的内存淤积
查 bytes dirty in the cache cumulative 要看累计值,不是瞬时值
很多人用 db.serverStatus().wiredTiger.cache 查到 bytes dirty in the cache cumulative 是个超大数字(比如 NumberLong("369249096605399")),误以为是当前脏页大小。其实它是**自实例启动以来所有脏页写入总量的累加和**,不能直接反映当前压力。
- 真正要看的是
bytes currently in the cache(当前 cache 占用字节数)和它占wiredTigerCacheSizeGB的百分比 - 配合
db.serverStatus().metrics.transaction中的currentActive和currentInactive才能定位是否为事务拖累 - 如果
currentActive × 平均文档大小 > 0.7 × wiredTigerCacheSizeGB(单位统一为字节),必须干预——这个阈值比看 %used 更早预警
别只盯着 %dirty,eviction 线程才是关键瓶颈
当 %dirty 持续超 20%,说明 WiredTiger 自身的 eviction 线程跟不上脏页生成速度。此时调高线程数可能缓解,但有代价:
- 默认
threads_max=4,可临时提升至8:db.runCommand({"setParameter":1, "wiredTigerEngineRuntimeConfig":"eviction=(threads_max=8,threads_min=4)"}) - 但线程越多,CPU 竞争越强,尤其在副本集主节点上,可能影响 oplog 应用速度
- 这只是“加速清淤”,不是“减少造淤”——根本解法仍是压事务粒度、拆批、禁用
find().forEach()类循环更新
事务最危险的地方在于:它让内存增长变得不可见。一条日志写着 “transaction completed”,但快照可能还在 cache 里赖着没被回收——因为 eviction 线程忙不过来,或者别的事务正锁着资源。盯住 currentActive 和 bytes currently in the cache 的组合,比任何单点指标都管用。


















