innodb_lock_waits是唯一能直接反映“谁在等谁”的实时锁等待表,需立即查询;重点字段为requesting_trx_id(等待事务)和blocking_trx_id(阻塞事务);mysql 8.0+推荐用sys.innodb_lock_waits获取更直观的blocking_pid/waiting_pid。

直接查 INNODB_LOCK_WAITS 拿到阻塞关系
锁等待发生时,INNODB_LOCK_WAITS 是唯一能直接告诉你“谁在等谁”的系统表。它一有数据,就说明当前存在活跃的锁等待链,必须立刻查——这个表是快照,锁一释放就清空,稍晚一步就变空了。
执行:SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
重点看两个字段:
-
requesting_trx_id:正在等锁的事务 ID(受害者) -
blocking_trx_id:真正占着锁不放的事务 ID(元凶)
注意:MySQL 8.0+ 中该表仍可用,但字段名不如 sys.innodb_lock_waits 直观;后者直接提供 blocking_pid 和 waiting_pid,省去一次 ID 转换。
用 blocking_trx_id 查 INNODB_TRX 确认持锁状态
拿到 blocking_trx_id 后,立刻去查它在 INNODB_TRX 中的真实状态,别只看 PROCESSLIST——很多阻塞源线程状态是 Sleep,Info 为空,但事务没提交,锁就一直攥着。
执行:SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_id = 'xxx';
关键判断点:
-
trx_state = 'RUNNING'且trx_started时间远早于当前时间(比如 >60 秒),基本就是它 -
trx_query为空?正常。事务可能只执行了BEGIN或 SQL 已跑完但没COMMIT -
trx_state = 'LOCK WAIT'?说明它自己也被卡住了,锁链已形成,得继续往上追
关联 PROCESSLIST 找出真实 SQL 和可操作线程 ID
INNODB_TRX.trx_mysql_thread_id 是你在数据库里真正能动手的入口。把它填进 PROCESSLIST 查询,才能看到对应连接的完整上下文。
执行:SELECT ID, USER, HOST, COMMAND, TIME, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = ?;
重点关注这几类信号:
-
COMMAND = 'Sleep'且TIME > 60:应用拿了连接没关,极大概率挂着未提交事务 -
STATE = 'Updating'且TIME持续增长:SQL 卡在 I/O 或锁本身,不是慢查询就是索引失效 -
INFO字段被截断或为空:不代表没在执行,尤其用了预处理(如 PDO emulate mode)时常见
要 KILL,必须杀这里的 ID,不是 trx_id;优先用 KILL QUERY ID,避免中断整个连接影响其他语句。
别漏掉 Metadata Lock 这个隐形杀手
如果 PROCESSLIST 里大量线程卡在 Waiting for table metadata lock,那就不是行锁问题,而是 DDL 操作(如 ALTER TABLE)引发的元数据锁(MDL)阻塞。这种锁不会出现在 INNODB_LOCK_WAITS 里,也查不到 trx_query。
必须切到 performance_schema:
- 先确认启用:
SELECT * FROM performance_schema.setup_consumers WHERE NAME = 'global_instrumentation'; - 再查锁:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND LOCK_STATUS = 'PENDING';
MDL 锁的持有者往往是一条长时间运行的 SELECT、未关闭的事务,或正在执行的 DDL。它比行锁更隐蔽,也更容易被误判为“数据库卡了”。











