MongoDB事务需严格遵循操作顺序、避免跨分片、禁用非数据库操作、控制文档数在100以内、慎用高开销读操作;否则易引发锁等待、死锁或全局锁降级。

事务内操作顺序必须严格一致
多个事务如果以不同顺序修改同一组文档,WiredTiger 的文档级锁会迅速演变成锁等待甚至死锁。MongoDB 不会像 MySQL 那样自动回滚并报 LATEST DETECTED DEADLOCK,而是让后到的事务长时间阻塞在 Waiting for lock 状态,监控里看到的是 WT_ROLLBACK 或持续增长的 lock.currentQueue.writers。
实操建议:
- 所有业务代码中,对同一业务实体(如订单+支付+物流)的更新,强制按固定字段排序后再执行:比如先按
order_id升序处理所有相关文档,再批量提交 - 避免「先改用户状态,再写日志」和「先写日志,再改用户状态」两种逻辑混用;统一收敛到一个服务层做顺序编排
- 在分片集群中,事务只能跨集合但不能跨分片——若订单和用户表不在同一分片,
session.startTransaction()会直接抛出IllegalOperation: Cannot perform transaction with operations on multiple shards
别在事务里做非数据库操作
事务生命周期越长,文档锁持有时间就越久。WiredTiger 虽然是文档级锁,但一旦事务未提交,该文档就无法被其他写请求修改——哪怕只是读取带 $inc 的原子操作也会被卡住。
常见错误现象:
- 事务中调用 HTTP API 查询第三方状态,等 2 秒才继续
collection.updateOne() - 事务内解析 JSON、生成 PDF、压缩图片,导致锁持有时长达数百毫秒
- 用
db.runCommand({ 'eval': ... })执行 JS 脚本(已弃用且严重阻塞)
正确做法:
- 把网络/计算类操作全部移出事务外,只保留纯粹的 CRUD
- 需要依赖外部结果?先查好、缓存好、校验好,再进事务;或改用最终一致性 + 补偿事务
- 批量写入时,用
bulkWrite()替代循环单条updateOne(),减少事务内 round-trip 次数
避免多文档事务触发全局锁降级
WiredTiger 默认是文档级锁,但一旦事务涉及超过 1000 个文档(具体阈值受 wiredTigerEngineConfigString 中 transaction_max_batch_size 影响),引擎可能自动升级为集合级锁,甚至在极端情况下退化为数据库级等待。
关键参数与影响:
-
transaction_max_batch_size默认 1000,可在mongod.conf中调低(如设为 500),提前触发分批提交 - 事务中使用
$lookup或聚合管道,即使不写数据,也可能因临时集合扫描引发隐式锁扩大 - 分片集群下,事务中任意一个操作失败(如某分片不可达),整个事务回滚,但锁释放有延迟,造成下游等待雪崩
建议:
- 单事务操作文档数控制在 100 以内;超量时主动拆成多个
bulkWrite()+ 独立事务 - 禁用事务内的
$facet、$graphLookup等高开销阶段 - 用
db.currentOp({ "secs_running": { "$gt": 2 } })定期抓取长运行事务,结合lsid关联应用日志定位源头
读操作也会影响锁等待
很多人以为只有 update、delete 才加锁,其实 WiredTiger 在事务中执行带写意图的读(如 findAndModify、findOneAndUpdate)或带 writeConcern 的读(如 readConcern: "majority"),都会参与锁竞争。
容易踩的坑:
- 在事务里反复调用
findOneAndUpdate({ _id: x }, { $inc: { retry_count: 1 } })做幂等重试,每次都在抢同一文档锁 - 用
readConcern: "snapshot"读历史版本,虽不阻塞写,但会延长事务活跃时间,间接拉长锁持有窗口 - 事务中执行
countDocuments(),底层是全索引扫描,可能锁住大量间隙(尤其无索引时)
优化方向:
- 幂等写优先用
upsert+ 唯一索引,而不是靠事务内重试 - 统计类读操作移到事务外,或用预聚合集合(如
daily_stats)替代实时count() - 确认是否真需要
readConcern: "majority":多数场景用"local"足够,除非强依赖跨节点一致性
事务锁等待最隐蔽的点,往往不是“锁了多久”,而是“谁在等、等什么、为什么没释放”——db.currentOp() 输出里的 secs_running 和 numYields 比任何监控图表都真实。











