真正卡住ddl的是长时间未提交的事务,需查innodb_trx中trx_started超2分钟且trx_query为空的记录;再用sys.schema_table_lock_waits定位等待链,确认blocking_pid持mdl锁后执行kill pid释放锁。

查 INNODB_TRX 找真正卡住 DDL 的长事务
别只盯着 SHOW PROCESSLIST 里 Command = 'Sleep' 的连接——很多阻塞者状态是 Sleep,但 trx_state = 'RUNNING',且 trx_started 时间远早于现在。执行:SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 120;
重点看 trx_started 超过 2 分钟、trx_query 为空或只有简单 SELECT 的记录。这类事务大概率是 BEGIN 后忘了 COMMIT,MDL 锁从开启那一刻就一直挂着。
用 sys.schema_table_lock_waits 快速定位等待链
这是 MySQL 5.7+ 最省力的起点,一行查清谁在等、谁在堵:SELECT * FROM sys.schema_table_lock_waits\G
输出中关注:
• waiting_pid:被卡住的 DDL 线程(比如正在跑 ALTER TABLE)
• blocking_pid:真正持锁的线程 ID
• sql_kill_blocking_connection:可直接复制执行的 KILL 命令
注意:如果 sys 库不可用或权限不足,别硬试,立刻切到 performance_schema.metadata_locks 方案。
确认持锁类型再动手杀,别误伤长查询
KILL 前必须区分清楚:是空转事务,还是真在跑业务逻辑?
• 查 performance_schema.metadata_locks,过滤 LOCK_STATUS = 'GRANTED' 且 LOCK_TYPE 为 SHARED_READ 或 SHARED_WRITE 的记录,确认该线程确实在持 MDL 锁
• 再查 performance_schema.threads,匹配 PROCESSLIST_INFO:若为空或仅显示 SELECT,基本无实际操作;若显示 UPDATE ... WHERE 且 TRX_ROWS_MODIFIED > 0,需谨慎评估影响
• 用 SHOW ENGINE INNODB STATUS\G 查对应 trx_id,看 mysql tables in use 和 locked tables 是否都为 0 —— 是则可安全终止
KILL QUERY 对 MDL 无效,必须用 KILL
KILL QUERY <code>pid 只中断当前语句,事务仍活跃,MDL 锁不会释放。DDL 还会继续卡着。
正确做法:
• 先执行 KILL QUERY <code>pid(对 Sleep 连接虽无效,但可排除它正在执行长查询的可能)
• 等 10–20 秒,观察 State 是否变化
• 若仍是 Sleep 且 Time 持续增长,立即执行 KILL <code>pid
注意:KILL 会触发回滚,若事务已写大量 undo 日志,回滚本身可能耗时数分钟——这期间仍阻塞其他 DDL,所以提前确认是否真有必要杀。
真正容易被忽略的是:哪怕一条没带事务控制的 SELECT,只要连接处于 autocommit = 0 模式,它就开始持有 MDL 读锁,直到显式 COMMIT 或连接断开。这不是慢查询问题,是锁协议本身的刚性约束。











