MongoDB事务超时由服务端参数transactionLifetimeLimitSeconds控制,默认60秒,maxTimeMS仅作用于单个操作;该值决定WiredTiger快照保留时长,影响MVCC版本链内存占用,需通过db.currentOp定位长事务并拆分批量操作、剥离外部I/O、finally中显式abort来避免资源耗尽。

transactionLifetimeLimitSeconds 默认60秒不是凑巧
MongoDB 事务超时由服务端参数 transactionLifetimeLimitSeconds 控制,4.4+ 默认值就是 60 秒。这不是客户端能绕过的限制,maxTimeMS 对整个事务生命周期完全无效——它只约束单个命令(比如一次 updateOne),不延展事务本身。
这个值直接决定 WiredTiger 引擎要为该事务保留多旧的 MVCC 版本链:事务越长,快照越老,内存中堆积的旧版本文档就越多。一旦多个事务同时卡住,cache 压力会指数级上升。
- WiredTiger cache 使用率持续 >85%,且日志出现
cache overflow,大概率是快照滞留导致 - 通过
db.currentOp({ "secs_running": { "$gt": 5 } })可快速定位运行超 5 秒的事务 - 分片集群下更危险:一个慢分片会让整个跨分片事务挂起,其他分片空等,锁和连接全被占着
长事务让 MVCC 版本链“长出肿瘤”
MVCC 不是免费的。每个事务启动时,WiredTiger 会记录一个 atClusterTime 快照点,并为该事务期间所有读写维护对应版本链。事务未结束,这些旧版本就不能被 GC 清理。
典型恶化场景:
- 循环插入 10000 条文档却不分批 → 单事务产生上万版本节点,内存暴涨
- 事务内调用 HTTP 支付网关,网络抖动 30 秒 → 快照锁定 30 秒,所有依赖该时间戳的读都拖着旧数据
-
session.startTransaction()后没commitTransaction()也没abortTransaction()→ 会话悬空,快照保留至 session 超时(默认 30 分钟)
此时 wiredTiger.cache.maximum bytes configured 很可能被吃满,新写入被迫刷盘或阻塞。
怎么避免事务把 MVCC 拖垮
核心原则:不让事务变成“内存吸血鬼”。不是靠拉长 transactionLifetimeLimitSeconds,而是从逻辑层切断长快照依赖。
- 批量操作必须拆分,建议 100–500 条/事务,每批设
maxTimeMS: 5000防止单次卡死 - 外部 I/O(HTTP、文件、RPC)一律前置完成,结果存变量,事务内只做原子 DB 操作
- 所有自定义事务逻辑必须包在
try/catch/finally中,finally里无条件调session.abortTransaction()—— 即使已 commit 过,重复 abort 也安全 - 云服务(如阿里云 MongoDB、Atlas)通常锁死该参数,无法动态修改,只能重构:用业务状态字段(如
status: "step2_pending")衔接多个短事务
真正难处理的是“半死不活”的事务
它不报错,不提交,也不被 kill,只是卡在网络等待、锁竞争或应用层异常吞掉错误的位置。这种事务不会出现在 currentOp 的明显错误列表里,但会持续占用 session、锁、内存和 oplog 位置。
监控重点不是“有没有超时”,而是“有没有不该还活着的事务”:globalLock.currentQueue.writers > 0 + replLag 突增 + oplog 写入速率骤降,三者同时出现,基本可以断定有事务在 silently leak。


















