直接看locks.Global.acquireWaitCount与transactions.totalCommitted比值是否持续>5%来判断锁等待是否异常;acquireWaitCount是阻塞后成功获取锁的次数(非时长),真实等待时间需启用profiling分析acquireWaitTime及lockStats.timeAcquiringMicros。

直接看 locks.Global.acquireWaitCount 和 transactions.totalCommitted 这两个指标,再结合 WriteConflictException 的日志频率,就能定位事务锁等待是否异常——不是所有“高 acquireWaitCount”都代表问题,关键要看它和事务提交量的比值是否持续 > 5%。
怎么看锁等待时间的真实含义?
MongoDB 的 acquireWaitCount 统计的是「尝试获取锁但被阻塞后成功」的次数,不是等待时长本身。它藏在 serverStatus 的 locks 字段里,单位是“次”,不是毫秒。真正反映延迟的是 acquireWaitTime(纳秒级总和),但这个值默认不开启采集,必须手动启用:db.setProfilingLevel(2, { slowms: 100 }) 并配合 system.profile 分析慢操作中的锁等待字段。
-
acquireWaitCount突增 +transactions.totalCommitted下降 → 写冲突加剧,事务重试变多 -
acquireWaitCount高但totalCommitted同步高 → 可能只是短时并发压测,未必是瓶颈 - 只看
Global层锁等待,忽略Database或Collection级别 → 容易漏掉集合级热点(比如优惠券集合被高频更新)
WriteConflictException 出现频率怎么算才准?
不能只扫应用层日志。WiredTiger 自动重试最多 3 次,第 4 次失败才抛出 WriteConflictException 给客户端。所以真实冲突率 ≈ mongod 日志中 WriteConflict 行数 ÷ transactions.totalStarted,且要限定时间窗口(比如 5 分钟滑动窗口)。注意:分片集群中,每个分片都要单独采集,mongos 层不聚合这类错误。
- 用
grep "WriteConflict" /var/log/mongodb/mongod.log | tail -n 1000快速抽样 - 如果
transactions.totalStarted是 1200,WriteConflict日志有 60 条 → 冲突率约 5%,已到需干预阈值 - 事务内含
$inc但冲突率仍高 → 很可能更新的是同一文档(如{_id: "coupon_123"}),不是锁粒度问题,是业务逻辑热点
为什么 Grafana 里锁指标突然归零?
常见于启用了 transactionLifetimeLimitSeconds(默认 60 秒)且事务超时被强制中止。此时 transactions.totalAborted 会上升,但 acquireWaitCount 不再累加——因为超时事务直接跳过锁竞争阶段,走清理路径。这种情况在分片集群中更隐蔽:某个分片因网络抖动延迟响应,导致整个事务在协调节点超时。
- 检查
transactions.totalAborted是否同步上升 - 确认所有分片副本集都设置了统一的
transactionLifetimeLimitSeconds,否则会出现“部分分片接受长事务、部分拒绝”的不一致 - Oplog 中不会记录被中止的事务,所以
oplog.rs大小无明显变化,容易误判为“没写入”
最常被忽略的一点:acquireWaitCount 和 WriteConflictException 都不反映“锁持有时间”。一个事务持有一个文档锁 3 秒,期间阻塞了 20 个其他事务,但只产生 1 次 acquireWaitCount —— 这种长持有必须靠 profile 日志里的 lockStats.timeAcquiringMicros 字段抓,不能只盯全局计数器。

















