查 sys.schema_table_lock_waits 是 mysql 5.7+ 定位锁阻塞链最省力起点,它直接展示等待线程、持锁线程、阻塞时长及持锁 sql;若 blocking_sql 为空,多为隐式事务未提交所致。

查 sys.schema_table_lock_waits 快速拿到阻塞链
这是 MySQL 5.7+ 最省力的起点,它直接把“谁在等、谁在堵、堵了多久、堵的是哪条 SQL”列清楚。执行 SELECT * FROM sys.schema_table_lock_waits\G,重点看三列:BLOCKING_PID(持锁线程 ID)、WAITING_PID(卡住的线程 ID)、BLOCKING_SQL(持锁者最后执行的语句)。如果 BLOCKING_SQL 为空,大概率是隐式事务(比如只执行了 BEGIN 就断开)在持锁;如果不为空,比如是 SELECT ... FOR UPDATE 或 mysqldump,就能快速判断业务上下文。
用 performance_schema.metadata_locks 确认谁真正在持锁
SHOW PROCESSLIST 完全看不到 MDL 持锁者,因为它是服务层锁,不是存储引擎层锁。必须依赖 performance_schema.metadata_locks。先确认采集器已开:SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl',确保 ENABLED 和 TIMED 都是 YES。再查具体锁:SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID FROM performance_schema.metadata_locks m JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'GRANTED'。只关注 LOCK_STATUS = 'GRANTED' 的行,对应 PROCESSLIST_ID 就是真正在 hold 锁的线程。
联合 INNODB_TRX 和 PROCESSLIST 找“Sleep 却 RUNNING”的悬挂事务
绝大多数元数据锁阻塞源,不是正在跑 SQL 的线程,而是那种 COMMAND = 'Sleep'、INFO 为空、TIME > 300 的连接——它大概率显式开启了事务但没 COMMIT。单查 INNODB_TRX 会漏掉已断连但事务未清理的连接,必须 JOIN:SELECT t.trx_id, t.trx_started, t.trx_state, p.ID, p.USER, p.HOST, p.COMMAND, p.TIME, p.INFO FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING' ORDER BY t.trx_started。重点看 trx_started 时间:越早越可疑;trx_isolation_level = 'REPEATABLE READ' 时,哪怕只执行过一条 SELECT,事务一启就持有 MDL_SHARED_READ 锁。
KILL QUERY 优先于 KILL,但别对空事务抱幻想
拿到持锁线程 ID 后,别直接 KILL <code>pid。先试 KILL QUERY <code>pid,只中断当前语句,保留连接和事务状态。这对卡在长查询里的线程有效;但对 INFO 为空的悬挂事务基本无效——它根本没在执行语句,只是挂着事务。这时才考虑 KILL <code>pid。注意:KILL QUERY 不释放 MDL 锁,只有 KILL 才能真正终结连接并释放 S MDL。验证是否真该杀:查 SELECT trx_id, trx_state, trx_rows_modified FROM information_schema.INNODB_TRX WHERE trx_mysql_thread_id = <code>pid,如果 trx_rows_modified > 0,说明有未提交写入,kill 会触发回滚,耗时可能很长。
真正难处理的从来不是“怎么 kill”,而是“怎么确认那个 Sleep 线程到底有没有业务价值”——它可能是报表导出中途断开、也可能是连接池复用后忘了 commit,甚至只是开发在客户端敲完 BEGIN 就去开会了。这时候看 trx_started 时间戳比看任何其他字段都管用。











