慢查询日志中Query_time高但Rows_examined和Rows_sent少,且Lock_time接近Query_time,表明是锁等待型慢查询;需结合SHOW ENGINE INNODB STATUS或performance_schema定位阻塞源头。

慢查询日志里 Query_time 明显偏高,但 Rows_examined 很小、Rows_sent 也少,基本可以断定不是执行慢,而是卡在锁等待上——这是锁等待型慢查询最典型的信号。
看懂慢查询日志里的 Lock_time 字段
MySQL 慢查询日志每条记录都包含 Lock_time,它代表该 SQL 在获取锁(主要是行锁)上实际等待的总时间,单位秒,精度到微秒。这个值和 Query_time 是独立计算的:
Query_time = Lock_time + 执行时间 + 网络/解析等开销- 如果
Query_time是 8.2 秒,而Lock_time是 8.19 秒,那真实执行可能不到 0.02 秒 -
Lock_time为 0 或接近 0(如 0.000001),基本排除锁等待,转向执行计划或 I/O 问题 - 注意:
Lock_time只统计「等待锁」的时间,不包括持有锁的时间;事务未提交导致别人等你,你的日志里Lock_time是 0
结合 SHOW ENGINE INNODB STATUS 定位阻塞源头
光有 Lock_time 只知道“被卡了”,但不知道谁卡的你。这时必须查 InnoDB 实时状态:
- 执行
SHOW ENGINE INNODB STATUS\G(注意结尾\G格式化输出) - 重点找
TRANSACTIONS部分,里面会列出所有活跃事务,包括:Trx id、Trx state(比如LOCK WAIT)、Trx started、Trx wait_started - 再往下找
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:和*** (2) HOLDS THE LOCK(S):—— 这就是等待关系:事务 1 在等事务 2 持有的锁 - 事务 2 的
Trx mysql thread id对应SHOW PROCESSLIST中的ID,可直接看到它在跑什么 SQL、是否Command是Sleep且Time很大(典型未提交事务)
用 performance_schema 查实时锁等待链
相比 SHOW ENGINE INNODB STATUS 的快照式输出,performance_schema 能提供更结构化、可关联的锁等待视图(需提前开启):
- 确认已启用:
SELECT * FROM performance_schema.setup_consumers WHERE NAME = 'events_waits_current';返回ENABLED - 查当前锁等待:
SELECT * FROM performance_schema.data_lock_waits;—— 直接给出BLOCKING_ENGINE_TRANSACTION_ID和REQUESTING_ENGINE_TRANSACTION_ID - 关联事务详情:
SELECT * FROM performance_schema.events_transactions_current WHERE EVENT_ID = (SELECT EVENT_ID FROM performance_schema.data_lock_waits LIMIT 1); - 优势:可写脚本定时采集、支持 JOIN 多表关联(比如关联系统账号、应用 trace_id),适合接入监控体系
为什么不能只依赖 slow_query_log?
慢查询日志本身不记录「谁在持锁」,也不记录锁类型(S/X)、锁对象(哪一行、哪个间隙),更不会告诉你事务隔离级别或 autocommit 状态。一个 Lock_time: 3.567890 的日志,背后可能是:
- 另一个事务执行了
UPDATE users SET status=1 WHERE id=123后没COMMIT - 一个长事务在做范围扫描,持有了
gap lock,阻塞了后续插入 - 应用层用了
SELECT ... FOR UPDATE但异常退出没回滚 - 甚至可能是低效的
SELECT触发了 MVCC 版本链遍历,间接拉长了锁等待窗口
这些细节,必须靠 INFORMATION_SCHEMA.INNODB_TRX、INNODB_LOCKS(MySQL 5.7)或 performance_schema.data_locks(MySQL 8.0+)交叉验证。别跳过这一步——日志里看到的“慢”,往往只是冰山露出水面的十分之一。


















