慢查询日志不直接记录事务锁等待,但可通过select执行时间异常偏高、update/delete耗时波动大、频繁出现metadata lock等待等间接迹象暴露长事务;需调低long_query_time至0.2~1.0秒并配合innodb_trx和performance_schema.data_locks定位锁等待根源。

慢查询日志本身不记录事务锁等待,但能暴露长事务的间接证据
MySQL 的 slow_query_log 只记录执行时间超过 long_query_time 的语句,它不会直接写“这个 SELECT 被锁了 8 秒”,但大量出现以下情况时,大概率存在未提交事务导致的锁等待:
-
SELECT语句执行时间异常偏高(比如平时 10ms,突然变成 2s+),且该语句本身无复杂 JOIN 或大范围扫描 - 同一条
UPDATE/DELETE出现在慢日志中多次,每次耗时波动极大(如 50ms → 3s → 800ms) - 慢日志里频繁出现
Waiting for table metadata lock或Waiting for global read lock(这类等待会卡住整个语句,计入慢日志)
关键点:慢日志是“症状日志”,不是“病因日志”。它提示你“这里可能有锁问题”,但不能替代 INFORMATION_SCHEMA.INNODB_TRX 或 performance_schema.data_locks 去定位谁在等、谁在占。
必须开启并配置 slow_query_log 才能捕获真实慢操作
默认 MySQL 通常关闭慢查询日志,且 long_query_time 默认为 10 秒——这对发现锁等待完全无效。锁等待往往发生在毫秒到秒级,需主动调低:
- 运行时临时启用:
SET GLOBAL slow_query_log = ON;SET GLOBAL long_query_time = 0.5(单位秒,建议设为 0.2~1.0) - 永久生效需在
my.cnf中添加:slow_query_log = 1、slow_query_log_file = /var/log/mysql/mysql-slow.log、long_query_time = 0.3 - 务必同时开启
log_queries_not_using_indexes = OFF(否则索引缺失的简单查询也会刷屏,掩盖真正问题)
注意:long_query_time 对已持有锁但尚未执行完的语句不生效——它只从语句真正开始执行计时。如果一个事务 BEGIN 后 5 秒才发 UPDATE,那 UPDATE 的耗时仍从执行那一刻算起。
结合 slow log 和 INNODB_TRX 才能确认是否为锁等待
单看慢日志只能怀疑,必须立刻查当前活跃事务状态。典型排查链路如下:
- 从慢日志中挑出一条高耗时的
SELECT,记下它的id(或客户端 IP + 时间戳) - 登录 MySQL,执行:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 3,找出运行超 3 秒的事务 - 对每个可疑
trx_id,查:SELECT * FROM performance_schema.data_locks WHERE ENGINE_TRANSACTION_ID = ?(MySQL 8.0+)或用SHOW ENGINE INNODB STATUS\G解析 LOCK WAIT 部分 - 重点看
trx_state = 'LOCK WAIT'和trx_wait_started字段——如果 wait 已持续数秒,而另一个事务trx_state = 'RUNNING'且trx_started早得多,基本就是它没提交
常见陷阱:应用层 ORM(如 Django/SQLAlchemy)可能自动开启事务但忘记 commit,或在 try/except 中吞掉异常导致 rollback 没执行。这类事务在 INNODB_TRX 里表现为长时间 RUNNING 却无后续语句。
用 pt-query-digest 快速聚合分析慢日志中的锁模式
手动翻日志效率低,pt-query-digest(Percona Toolkit)能帮你把“被锁住的语句”聚合成可读报告:
- 基础命令:
pt-query-digest /var/log/mysql/mysql-slow.log --filter '$event->{Lock_time} > 0.1' --limit 10(只看锁等待超 100ms 的语句) - 更准的过滤:
--filter '$event->{Rows_examined} {Query_time} > 1'(扫描行少但耗时长 → 极可能是锁等待) - 输出中重点关注 “Lock time” 列占比、“Query_time” 分布、“User” 和 “Host” 是否集中——若某应用服务 IP 占比超高,优先查它有没有未关闭事务
注意:pt-query-digest 依赖日志中 # Lock_time: 字段,而 MySQL 5.7+ 默认不记录该字段。需在配置中加 log_output = FILE 并确保 log_slow_verbosity = 'full'(或至少含 microseconds)才能让慢日志包含锁时间。
真正难的不是找到慢查询,而是判断它慢是因为磁盘 IO、CPU、网络,还是别的事务卡住了它。一旦怀疑是锁,别只盯着慢日志——立刻切到 INNODB_TRX 看实时状态,再回溯应用代码里事务边界是否清晰。很多长事务根本不在 SQL 层面,而在业务逻辑里跨了多个 HTTP 请求还没 commit。











