innodb_row_lock_waits是自启动累计的行锁等待次数,单看绝对值无法反映争用严重程度;必须结合单位时间增量(如>100次/分钟)、innodb_row_lock_current_waits(瞬时等待数>5)和innodb_row_lock_time_avg(平均等待>50ms)综合判断。

innodb_row_lock_waits 是什么,它真能反映锁争用严重程度?
innodb_row_lock_waits 是 InnoDB 存储引擎的全局状态变量,表示自实例启动以来发生行锁等待的总次数。它本身不是实时指标,也不带时间维度或等待时长信息——只计数。所以单看当前值(比如 1247)毫无意义,必须结合采样周期和增量变化来判断。
- 增量突增(如 5 秒内涨了 200+)往往比绝对值更能说明问题
- 若长期稳定在“每秒 0.1 次”,通常可忽略;若持续 > 5 次/秒,需警惕
- 它不区分等待是 1ms 还是 2s,所以必须配合
innodb_row_lock_time_avg和innodb_row_lock_waits的比值粗略估算平均等待耗时
注意:SHOW STATUS LIKE 'innodb_row_lock%' 返回的是整型累计值,直接读取后需自己做减法计算速率。
如何安全地采集并判断是否异常?
监控不能只靠一次 SHOW STATUS,必须定时抓差值。推荐用以下方式:
- 使用
mysqladmin extended-status -r -i 1 | grep innodb_row_lock(-r表示重复获取,-i 1表示间隔 1 秒),观察连续几轮的innodb_row_lock_waits增量 - 在业务低峰期跑一次基线:记录 60 秒内增量,再对比高峰期同一时段的增量倍数
- 若某次采样窗口内
innodb_row_lock_waits增量 ≥ 10 且innodb_row_lock_time_avg> 5000(单位微秒,即 5ms),基本可判定存在明显锁竞争
别直接用 Prometheus + mysqld_exporter 就完事——默认采集间隔(15s)太粗,容易漏掉尖峰;建议将采集频率调至 5s,并对 innodb_row_lock_waits 做 rate() 计算(如 rate(mysql_global_status_innodb_row_lock_waits[1m]))。
看到高值后,第一反应不该是查 SQL,而是确认是不是误报
很多高 innodb_row_lock_waits 并非业务逻辑问题,而是配置或使用姿势导致:
-
autocommit=0且事务未及时提交,会让锁持有时间远超预期 - 使用
SELECT ... FOR UPDATE但没走索引,触发全表扫描锁,极大增加冲突概率 - 应用层重试逻辑不当(如幂等更新失败后立即重试),在锁未释放时反复撞上同一行
-
innodb_lock_wait_timeout设置过小(如 1s),导致大量事务快速超时退出,反而推高innodb_row_lock_waits计数(每次等待都计入,哪怕没等到锁)
先执行 SELECT * FROM information_schema.INNODB_TRX\; 看活跃事务列表,重点关注 TRX_STATE = 'LOCK WAIT' 和 TRX_MYSQL_THREAD_ID,再关联 PROCESSLIST 找出源头连接。
为什么光盯这个指标会漏掉更危险的问题?
innodb_row_lock_waits 只统计“进入等待队列”的次数,但以下情况它完全不感知:
- 事务因死锁被自动回滚(记录在
innodb_deadlocks,不计入此值) - 行锁升级为表锁(如 ALTER TABLE 期间的元数据锁阻塞,不经过行锁路径)
- 高并发下大量线程在进入 InnoDB 层前就卡在连接池或网络层,根本没走到锁逻辑
真正有压力的系统,往往 innodb_row_lock_waits 还没飙升,Threads_running 已持续 > 50,或者 Innodb_buffer_pool_wait_free 开始非零——这些才是更早的窒息信号。











