ddl卡在waiting for table metadata lock时,真正需杀的是持锁事务而非等待线程;应通过performance_schema.metadata_locks定位lock_status='granted'的持锁连接,并优先kill query再kill以避免长回滚。

DDL卡在 Waiting for table metadata lock,不是表被锁死了,而是某个连接正拿着元数据锁(MDL)不放——真正要杀的,从来不是那个显示“等待中”的线程,而是背后那个安静持锁的事务。
查 performance_schema.metadata_locks 定位持锁线程
MySQL 5.7+ 的 MDL 持有关系不会出现在 SHOW PROCESSLIST 的 State 字段里,必须靠 performance_schema.metadata_locks 查。先确认它已启用:
-
SELECT @@performance_schema;返回1才有效 -
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';中ENABLED和TIMED都应为YES;若否,执行:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
再查具体锁:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST 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';
重点关注 LOCK_STATUS = 'GRANTED' 且 LOCK_TYPE 是 SHARED_READ 或 SHARED_WRITE 的行——这些 PROCESSLIST_ID 就是真正在持锁的连接 ID。
识别 Command='Sleep' 却仍在 RUNNING 的悬挂事务
最常卡住 DDL 的,不是正在跑慢查询的线程,而是 Command = 'Sleep'、State 为空、但 Time 超过 300 秒的连接。它往往对应一个 BEGIN 了却没 COMMIT 或 ROLLBACK 的事务,MDL 锁从 BEGIN 开始就一直挂着。
用这个 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;
-
TIME > 300且INFO为空 → 极大概率是应用异常中断或忘记 commit -
trx_isolation_level = 'REPEATABLE READ'→ 即使只执行过一次SELECT,事务一启动就持 MDL 锁 - 别只看
trx_mysql_thread_id,结合p.HOST和p.USER定位到具体应用实例或 DBA 终端
KILL QUERY 之后再 KILL,避免回滚拖更久
对一个挂起的事务连接直接执行 KILL thread_id,会强制断连并回滚整个事务——如果 undo 日志很大,回滚本身可能耗时数分钟,期间仍阻塞其他 DDL。
- 先执行
KILL QUERY thread_id:对 Sleep 连接虽无效,但可试探是否真有长查询在跑;若连接实际在执行语句,这一步就能提前释放 MDL - 等 10–20 秒,观察
PROCESSLIST中该线程的State是否变化 - 若仍是
Sleep且Time持续增长,再执行KILL thread_id - 注意:某些 ORM(如 Hibernate)在事务异常中断后可能残留脏状态,KILL 前最好确认该连接当前无关键业务请求
ALGORITHM=INSTANT 并不万能,别被名字骗了
ALTER TABLE ... ALGORITHM=INSTANT 确实跳过 MDL 排他锁,但它只支持三类操作:
ADD COLUMNRENAME COLUMNALTER COLUMN SET DEFAULT
一旦涉及数据变更——比如加索引、改列类型、删列、修改 NOT NULL 约束——就会退化为 COPY 或 INPLACE,照样需要 MDL_EXCLUSIVE 锁,照样会被未提交事务阻塞。
真正容易被忽略的点是:哪怕你用了 INSTANT,只要目标表上存在任何未提交事务(哪怕只是个 SELECT),后续对该表的其他 DDL(比如另一个 ADD COLUMN)仍可能因锁队列排队而卡住——MDL 锁的调度是按请求顺序来的,不是谁快谁先。











