查不到“持有锁最久的sql”是因为data_lock_waits仅记录瞬时等待关系,不含持锁时间;需联合innodb_trx(查trx_started推算持锁时长)和data_locks(确认锁对象)三步联动定位。

查不到“持有锁最久的SQL”,因为 data_lock_waits 本身不记录持有时间,它只告诉你“此刻谁在等谁”——而“谁持有了多久”,得靠 INNODB_TRX 和 data_locks 联合推断。
为什么不能直接从 data_lock_waits 看出“持锁时长”
data_lock_waits 是瞬时快照表,只反映当前正在发生的等待关系。它的字段如 BLOCKING_ENGINE_TRANSACTION_ID 或 BLOCKING_THREAD_ID 指向一个事务或线程,但不包含该事务启动时间、是否空闲、有没有执行 SQL ——这些全得去别处查。
- 常见误操作:执行
SELECT * FROM performance_schema.data_lock_waits后盯着BLOCKING_THREAD_ID就去KILL,结果杀掉的是一个刚连上来做健康检查的探针 -
data_lock_waits的BLOCKING_ENGINE_LOCK_ID可能对应多个data_locks记录(比如同时持了行锁 + 间隙锁),但没时间戳字段 - MySQL 不提供“锁持有秒数”直出指标,必须用
trx_started时间倒推
真正能定位“持锁最久 SQL”的三步联动
核心逻辑:先从 data_lock_waits 找出被等的 BLOCKING_ENGINE_TRANSACTION_ID → 关联 INNODB_TRX 查该事务的 TRX_STARTED 和 TRX_QUERY → 再用 data_locks 确认它锁了哪些具体行
- 第一步:获取所有阻塞者事务 ID
SELECT DISTINCT BLOCKING_ENGINE_TRANSACTION_ID FROM performance_schema.data_lock_waits - 第二步:查这些事务的详情(重点关注
TRX_STARTED和TRX_QUERY)SELECT TRX_ID, TRX_MYSQL_THREAD_ID, TRX_STARTED, TRX_STATE, TRX_QUERY, TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) AS held_seconds FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_ID IN (SELECT DISTINCT BLOCKING_ENGINE_TRANSACTION_ID FROM performance_schema.data_lock_waits) ORDER BY held_seconds DESC LIMIT 5 - 第三步:对 top 1 的
TRX_ID,查它持有的锁细节SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE ENGINE_TRANSACTION_ID = ?
容易被忽略的关键点:空闲事务比慢 SQL 更危险
大量锁等待不是因为某条 UPDATE 很慢,而是某个事务早就该结束了却一直挂着 —— 比如应用异常断开、ORM 未正确关闭事务、或代码里漏了 COMMIT/ROLLBACK。这类事务在 INNODB_TRX 中表现为:
-
TRX_STATE = 'RUNNING'(不是LOCK WAIT) -
TRX_QUERY IS NULL或为空字符串 TRX_OPERATION_STATE = 'sleeping before entering InnoDB'-
TRX_STARTED时间远早于当前时间(比如 > 300 秒)
这时候 data_lock_waits 能告诉你“谁在等”,但只有结合 INNODB_TRX 的空闲状态和启动时间,才能判断出“这个持锁者根本没在干活”。











