真正要kill的是持有lock_status='granted'的持锁连接,而非显示“waiting for table metadata lock”的等待线程;该连接常为command='sleep'、事务未提交、mdl锁持续占用,须通过performance_schema.metadata_locks定位并kill connection释放。

DDL卡在Waiting for table metadata lock,该杀哪个线程
真正要KILL的不是显示Waiting for table metadata lock的那个线程,而是背后持有LOCK_STATUS = 'GRANTED'的连接。这个连接往往状态是Command = 'Sleep'、State = '',但事务未提交,MDL锁一直挂着。
常见错误现象:执行ALTER TABLE后卡住,SHOW PROCESSLIST里只看到等待线程,却找不到“正在干活”的源头。
- 必须查
performance_schema.metadata_locks,不能只依赖INNODB_TRX或PROCESSLIST -
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'GRANTED'—— 这些PROCESSLIST_ID才是持锁者 - 如果
LOCK_DURATION = 'TRANSACTION',说明锁生命周期绑定事务,不提交就永不释放
为什么KILL QUERY没用,必须KILL整个连接
KILL QUERY <code>pid只中断当前语句,事务仍活跃,MDL锁继续持有;只有KILL <code>pid才能终止连接、回滚事务、释放MDL。
使用场景差异明显:
- 阻塞者SQL是
SELECT ... FOR UPDATE且运行超10分钟 → 可直接KILL - 阻塞者SQL为空、
TRX_STATE = 'RUNNING'但TRX_ROWS_MODIFIED = 0→ 很可能是应用层卡在COMMIT前,需确认后杀 - 阻塞者是
mysqldump --single-transaction进程 → 杀之前先评估备份是否可中断
MySQL 5.7+ 必须启用performance_schema.mdl监控
默认情况下wait/lock/metadata/sql/mdl可能被禁用,导致metadata_locks表为空或无数据。
实操步骤:
- 确认启用:
SELECT @@performance_schema;返回1才有效 - 检查仪器开关:
SELECT ENABLED, TIMED FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 若未启用,执行:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 重启后生效(部分版本需重启mysqld)
ALGORITHM=INPLACE也卡住?元数据阶段照样要X锁
ALGORITHM=INPLACE或LOCK=NONE只优化数据重写阶段,DDL的元数据变更(打开表、更新字典、写binlog)始终需要短暂MDL_EXCLUSIVE锁——只要前面有未释放的MDL_SHARED_READ,它就必须排队。
容易被忽略的隐式持锁场景:
-
autocommit = 0下只执行INSERT没COMMIT,连接保持Sleep但锁还在 - ORM框架自动开启事务做预热查询(如Hibernate的
SELECT 1),事务未关闭 -
SELECT * FROM information_schema.COLUMNS这类系统表查询也会触发MDL,且持续时间短但频率高
最危险的是那些看不见的持锁者:没有慢查询、没有报错、SHOW PROCESSLIST里干干净净,但metadata_locks里LOCK_STATUS = 'GRANTED'的记录一直存在。











