<p>MySQL判断是否写入慢日志依据的是Query_time - Lock_time(纯执行时间),而非包含锁等待的Query_time;日志中Query_time记录的是总墙钟时间,但触发条件仅看实际执行耗时是否超过long_query_time。</p>

它不包含等待锁的时间——这是最常被误解的一点。 MySQL 判断是否写入慢查询日志,依据的是 Query_time - Lock_time(即纯执行时间),而非 Query_time 本身;但日志里记录的 Query_time 值,确实是包含了 Lock_time 的总耗时。
为什么日志里 Query_time 看起来“包含锁等待”?
因为 MySQL 在语句真正执行完毕、锁全部释放后,才打时间戳并写日志。此时它记录的是从语句开始解析到完全结束的完整墙钟时间(wall-clock time),自然覆盖了中间所有锁等待段。但决定“要不要记”的逻辑,是在执行过程中实时计算的:Query_time - Lock_time > long_query_time 才触发记录。
-
Query_time是总耗时(执行 + 锁等待),单位秒,精度微秒 -
Lock_time是该语句在 MySQL 层和 InnoDB 层累计的锁等待时间(含 MDL、InnoDB 行锁、MyISAM 表锁等) - 真正用于判断是否进 slow log 的,是
Query_time - Lock_time这个差值
常见误判场景:Lock_time 高但没进慢日志
比如一条 SELECT ... FOR UPDATE 实际执行只花了 20ms,但因前面事务持锁,它等了 8 秒才拿到行锁。此时 Query_time = 8.02s,Lock_time ≈ 8.0s,差值仅 0.02s。若 long_query_time = 1,它不会被记录——哪怕你肉眼看到它卡了 8 秒。
- 这种 SQL 在
SHOW PROCESSLIST中状态为Waiting for table level lock或Updating(InnoDB 行锁等待不显式显示状态,但Time字段会持续增长) - 它不会出现在 slow log,也不会被
mysqldumpslow统计到 - 排查必须靠实时抓取
INNODB_TRX+INNODB_LOCK_WAITS(5.7)或performance_schema.data_locks(8.0+)
Lock_time 具体包含哪些锁?
Lock_time 不是单一来源,而是多层锁等待的累加:
- MySQL 层:MDL 锁(
Waiting for table metadata lock)、MyISAM 表锁(Waiting for table level lock) - InnoDB 层:行锁等待(X/S lock on record)、间隙锁(gap lock)阻塞、外键检查引发的隐式锁
- 注意:
innodb_table_locks = 1且AUTOCOMMIT = 0时,LOCK TABLES可能引入 InnoDB 表级锁,也会计入Lock_time
真正要盯住的关键点
别只看 slow log 里的 Query_time 和 Lock_time 数值。当发现业务响应变慢但 slow log 没新增记录,或者 Lock_time 明显大于 Query_time(不可能,说明日志解析错位或版本异常),第一反应应是:
- 立刻查
SHOW FULL PROCESSLIST,过滤State LIKE '%lock%'和Time > 2的线程 - 确认这些线程涉及的表引擎:
SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA='xxx' AND TABLE_NAME='yyy'; - 如果是 MyISAM,重点看是否启用了
myisam_recover_options,修复动作会引发长时间独占锁 - 如果是 InnoDB,用
SELECT * FROM performance_schema.data_locks(8.0)或INNODB_LOCKS(5.7)定位具体锁在哪一行、哪个索引上
锁等待问题从来不在慢日志里“明说”,它藏在实时状态和锁视图里——等你主动去翻。


















