真正要杀的是 COMMAND='Sleep' 但 trx_state='RUNNING' 的悬挂事务;优先 KILL QUERY 而非 KILL;通过 sys.schema_table_lock_waits 或 performance_schema.metadata_locks 定位持锁者,再 JOIN INNODB_TRX 与 PROCESSLIST 精准识别。

真正要杀的不是显示 Waiting for table metadata lock 的线程,而是背后那个 COMMAND = 'Sleep' 却仍 trx_state = 'RUNNING' 的悬挂事务;优先执行 KILL QUERY,不是 KILL。
查 sys.schema_table_lock_waits 快速定位阻塞链
这是 MySQL 5.7+ 最直接的入口,一行 SQL 就能看清谁堵了谁:
它返回的 blocking_pid 是真正在持锁的连接 ID,waiting_pid 是你看到的卡住的 DDL 或查询。
如果返回为空,先确认两件事:
-
SELECT @@performance_schema;必须返回1 -
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';中ENABLED和TIMED都得是'YES'
若 blocking_pid 为 NULL,不代表没锁——很可能是隐式事务(比如客户端执行了 BEGIN 后只跑了一条 SELECT 就断开),仍在持 MDL_SHARED_READ 锁。
用 performance_schema.metadata_locks 看真实持锁状态
SHOW PROCESSLIST 根本不显示谁在 hold MDL 锁,因为它是服务层锁,不是 InnoDB 行锁。必须查这个表:
- 先确保采集器已开(同上)
- 再执行:
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_TYPE IN ('SHARED_READ', 'SHARED_WRITE')的行,对应PROCESSLIST_ID就是持锁者 - 拿这个 ID 去查
information_schema.PROCESSLIST:如果COMMAND = 'Sleep'、TIME > 300、INFO为空 → 几乎可以确定是连接池归还了连接但没COMMIT
联合 INNODB_TRX 和 PROCESSLIST 找“Sleep 却 RUNNING”的悬挂事务
单查 INNODB_TRX 会漏掉已断连但事务未清理的连接;单看 PROCESSLIST 又容易把真空闲连接当嫌疑对象。必须 JOIN:
- 执行:
SELECT t.trx_id, t.trx_started, t.trx_state, t.trx_isolation_level, p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.STATE, 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_isolation_level = 'REPEATABLE READ'的记录:哪怕只执行过一条SELECT,事务一启就持MDL_SHARED_READ锁 -
TIME > 300且INFO为空 → 极大概率是应用异常中断或忘记COMMIT
杀连接前先 KILL QUERY,别一上来就 KILL
对查到的 blocking_pid,优先执行:KILL QUERY blocking_pid;
而不是 KILL blocking_pid;。原因很实际:
-
KILL QUERY只中断当前语句,事务仍保持打开,锁可能还在(但多数场景下能释放 MDL) -
KILL会强制回滚整个事务,释放所有锁,但代价是业务侧可能收到连接中断错误 - 某些情况下(如 mysqldump --single-transaction 持锁),
KILL QUERY无效,才需KILL - 生产环境建议加个
SLEEP(1)再查一次metadata_locks,确认锁是否已释放,避免重复操作
最易被忽略的一点:很多悬挂事务来自开发测试时手动 BEGIN 后忘了 COMMIT 或 ROLLBACK,这类连接在 PROCESSLIST 里安静得像不存在,却能把整张表锁死数小时——必须靠 JOIN 查,不能只信 SHOW PROCESSLIST 的表象。


















