事务CPU高主因是未索引查询、snapshot读关注、跨分片协调及聚合误用;应建索引、降级readConcern、单分片操作、禁用事务内聚合。

事务中未加索引的 find 或 update 会触发全集合扫描
MongoDB 事务本身不直接消耗大量 CPU,但事务内执行的查询若缺少有效索引,会在事务持有锁期间持续扫描大量文档,导致 mongod 线程在内存中反复过滤、比较、跳过——这部分逻辑全由 CPU 承担。尤其当事务包含 find({ status: "pending" }) 这类低选择性条件,又没在 status 字段建索引时,一次事务就可能扫几百万文档。
实操建议:
- 用
db.currentOp({ "secs_running": { "$gt": 2 } })查看运行超 2 秒的活跃事务,重点关注secs_running高且numYields持续增长的操作 - 对事务内所有
find、updateOne、deleteMany的查询条件字段,逐个检查是否命中索引:执行db.collection.explain("executionStats").find({...}),确认executionStats.nReturned远小于executionStats.totalDocsExamined就是问题信号 - 避免在事务里做“先查后判再改”的链式逻辑,比如
find().toArray()后用 JS 循环过滤——这会让全部数据拉到应用层,事务锁期拉长且毫无必要
readConcern: "snapshot" 在高写入场景下显著增加 CPU 开销
MongoDB 多文档事务默认使用 readConcern: "snapshot",它要求事务视图必须基于某个一致的时间点快照。当集合写入频繁时,存储引擎(WiredTiger)需维护大量历史版本页(update log),CPU 要花额外 cycles 做版本链遍历和可见性判断。这不是 bug,而是隔离级别代价。
实操建议:
- 确认是否真需要事务级一致性:如果业务允许读已提交(RC),可显式降级为
readConcern: "majority",大幅减少版本管理压力 - 检查事务生命周期:长时间空闲的事务(比如 await 外部 HTTP 调用)会拖住快照不释放,用
db.killOp(opId)主动终结可疑长事务 - 监控
wt_cache_overhead和wt_cache_bytes_dirty指标,这两个值持续偏高往往意味着 snapshot 压力正在传导为 CPU 负载
嵌套事务或跨分片事务引发协调器开销激增
单机 MongoDB 不支持嵌套事务,但应用层误用(如外层 try/catch 包裹内层 session.startTransaction())可能造成 session 复用混乱;而分片集群中,涉及多个 shard 的事务会启动一个 coordinator 节点统一调度,每次 prepare/commit 都要跨网络广播并等待 ACK,CPU 会集中在 coordinator 的事件循环和序列化处理上。
实操建议:
- 禁止在循环体或递归函数中无条件调用
session.startTransaction(),务必确保每个 session 最多一个活跃事务 - 分片键设计要尽量让事务操作落在单个 shard 上:比如订单事务以
orderId为分片键,就别在事务里同时查users集合(除非该集合是 unsharded) - 用
sh.getBalancerStatus()和sh.status()确认事务相关集合是否真的分片了——有时误将小集合分片反而放大协调成本
聚合管道误入事务:$lookup + $unwind 组合极易失控
聚合阶段如 $lookup 若未命中被关联集合的索引,或 $unwind 后产生爆炸式文档膨胀,会在事务上下文中完成全部计算。更危险的是,某些驱动(如 Node.js 的 mongodb@4.x)在事务 session 中执行 collection.aggregate() 时不会自动拒绝,但实际会把整个 pipeline 锁在事务内执行,CPU 直接飙高。
实操建议:
- 事务内禁用
$lookup,改用应用层两次查询 + 关联逻辑;必须用时,确保被 lookup 的字段有唯一索引,且加maxDocuments限制输出 - 用
explain("executionStats")测试聚合管道:关注executionStats.nReturned和executionStats.totalKeysExamined是否成比例,若后者远大于前者,说明索引失效 - 避免在事务中执行带
$facet或多分支$cond的复杂聚合——这些操作无法下推到索引层,纯靠 CPU 迭代
explain 输出里的 executionTimeMillis 在事务中会失真,必须结合 currentOp 和系统级 top -Hp 定位真实热点线程。

















