必须先定位真正持有mdl锁的源头会话再kill,而非杀waiting for table metadata lock的等待线程;优先查performance_schema.metadata_locks中lock_duration='transaction'且lock_status='granted'的行,结合threads表获取processlist_id,确认后执行kill释放锁。

直接杀错线程会让积压更严重,必须先定位真正持有 MDL 锁的源头会话,再用 KILL 终止其连接——不是杀那些显示 Waiting for table metadata lock 的等待线程。
怎么快速确认哪个线程在“占着锁不放”
别只看 SHOW PROCESSLIST,它不显示锁归属。优先查 performance_schema.metadata_locks:
- 先确保 MDL 监控已启用:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 查对目标表持锁的会话(替换
your_db和your_table):SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, 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_DURATION = 'TRANSACTION'的行——这种锁不会随语句结束释放,只随事务提交或回滚才松手
为什么不能只查 INNODB_TRX 就动手杀
INNODB_TRX 能筛出长事务,但无法直接告诉你它锁了哪张表。容易漏掉两种关键情况:
- 事务里只执行了
BEGIN或SELECT ... FOR UPDATE,TRX_QUERY为空,但TRX_STATE = 'RUNNING'且TRX_STARTED很早,它照样持有 S 级 MDL - 应用层 autocommit=0 下执行完 DML 就断开,没显式
COMMIT或ROLLBACK,这个连接还活着,锁就挂着 - 必须把
TRX_MYSQL_THREAD_ID和performance_schema.threads.PROCESSLIST_ID关联起来,才能确认真实会话身份
如何避免误杀、精准释放锁
KILL QUERY 没用,它只中断当前语句,事务还在跑,MDL 锁照旧持有。必须用 KILL 强制断连:
- 拿到
PROCESSLIST_ID后,先执行:SELECT * FROM performance_schema.threads WHERE PROCESSLIST_ID = <code>pid; 看PROCESSLIST_INFO是否为空、连接是否异常 - 确认无误后执行:
KILL <code>pid; ——这会触发事务回滚,立即释放所有 MDL - 如果用的是 MySQL 8.0+,直接跑:
SELECT * FROM sys.schema_table_lock_waits\G,BLOCKING_PID字段就是能直接KILL的 ID,结果比手拼表直观得多
DDL 卡住时最常被忽略的细节
很多人看到 ALTER TABLE 卡住,第一反应是查它自己,却忘了它只是雪崩链的中间一环。真正该盯的是那个最早持锁、但看起来“什么都没干”的会话——比如一个凌晨三点开始、持续两小时没提交的报表查询,或者一个因网络闪断而僵死的连接。这类线程在 SHOW PROCESSLIST 里状态可能是 Sleep,但只要事务没结束,它的 MDL 就一直在拦路。











