应使用innodb_trx表的trx_wait_started字段计算实时等待时长:select trx_id, trx_mysql_thread_id, trx_query, unix_timestamp(now()) - unix_timestamp(trx_wait_started) as wait_seconds from information_schema.innodb_trx where trx_state = 'lock wait';

查InnoDB当前锁等待耗时:别信INNODB_LOCK_WAITS,用trx_wait_started
MySQL 8.0+ 中 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 已为空,查它永远返回空——这不是你权限或SQL写错了,是它被废弃了。真正能算出“当前等了多久”的字段,是 INNODB_TRX.trx_wait_started。
执行这条语句就能拿到秒级等待时长:
SELECT trx_id, trx_mysql_thread_id, trx_query, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(trx_wait_started) AS wait_seconds FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT';
-
trx_wait_started是 MySQL 5.7+ 引入的精确时间戳字段,类型为DATETIME,比靠NOW() - trx_started算总事务时长靠谱得多 - 结果为空 ≠ 没锁等待,可能只是没事务卡在
LOCK WAIT状态(比如已超时回滚,或刚释放) - 普通账号默认查不到其他用户事务,需授予
PROCESS权限;否则只看到自己线程,容易误判
查MyISAM锁等待耗时:它根本没“等待耗时”概念
MyISAM 没事务、没行锁、没等待队列,它的“锁”是表级的、阻塞式的、不可中断的。所谓“等待”,其实是操作系统层面的线程挂起,MySQL 不记录任何等待开始时间。
你看到的 SHOW OPEN TABLES WHERE In_use > 0 返回的表,只是表示该表正被某个线程打开并持有读/写锁——但你无法知道这个锁持有了多久、谁在等、等了多久。
- 如果某条
SELECT或UPDATE卡住,大概率是前面有个慢查询或崩溃未清理的锁,而不是“正在等”;In_use=1对 MyISAM 表甚至可能只是个简单SELECT,不代表有问题 - 想确认是否损坏,必须用
CHECK TABLE table_name;返回status: OK才算健康,否则要REPAIR TABLE - 监控重点不是“耗时”,而是
Key_reads / Key_read_requests比值——超过0.01就说明key_buffer_size不够,索引缓存失效导致频繁磁盘读,间接拖慢所有操作
为什么innodb_row_lock_time_avg不准?它不是“当前等待时长”
innodb_row_lock_time_avg 是全局累计平均值,单位毫秒,来自 SHOW STATUS。它反映的是自实例启动以来所有行锁等待的平均耗时,不是此刻某条事务的等待时间。
- 它会受历史抖动污染:比如昨天有次大促出现过 2 秒等待,今天全量平均还是 15ms,看不出当前异常
- 它不区分事务:一个长事务等了 30 秒,和一百个 1ms 等待混在一起,数值完全失真
- 真正用于告警的指标,必须是
NOW() - trx_wait_started的实时差值,且要按事务粒度上报(如 Prometheus 的mysql_innodb_trx_wait_seconds)
跨引擎对比监控时最容易忽略的一点
同一个库中混合使用 InnoDB 和 MyISAM 表时,INFORMATION_SCHEMA.INNODB_TRX 查不到 MyISAM 表的任何锁信息,而 SHOW OPEN TABLES 又对 InnoDB 行锁完全不敏感——你必须同时跑两套逻辑,且不能互相替代。
更麻烦的是:INFORMATION_SCHEMA 查询本身会加 MDL 锁,高并发下每秒刷一次 INNODB_TRX 可能反成性能瓶颈;建议采集间隔设为 5–10 秒,配合 innodb_row_lock_current_waits > 0 做前置触发。











