答案是:通过SHOW PROCESSLIST查看State为“Waiting for table metadata lock”且Info为DDL语句,再结合performance_schema.metadata_locks定位持有锁的会话。

如何快速确认是 MDL 锁导致 DDL 卡死
DDL 卡住时,SHOW PROCESSLIST 里最典型的信号是:State 显示 Waiting for table metadata lock,且 Info 列显示的是 ALTER TABLE、DROP TABLE 等 DDL 语句。这说明当前 DDL 正在等某个会话持有的 MDL 锁释放。
但注意:持有锁的会话未必在 PROCESSLIST 里显眼——它可能处于 Sleep 状态,却仍开着一个未提交的事务(比如开了事务后执行了 SELECT ... FOR UPDATE,然后忘了 COMMIT)。
排查建议:
- 运行
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING',找到被阻塞的锁请求 - 再查
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED',比对OBJECT_SCHEMA和OBJECT_NAME,定位谁持有了同表的锁 - 结合
performance_schema.threads和information_schema.PROCESSLIST关联THREAD_ID,找出对应连接的ID和USER
KILL 操作前必须验证的三个条件
盲目 KILL 可能中断正在写 binlog 的事务,或导致从库复制中断。执行前务必确认:
- 目标会话是否已无业务逻辑在跑(
COMMAND是Sleep且TIME超过 300 秒,大概率可杀) - 该会话是否属于应用连接池里的空闲连接(查
USER和HOST,避免误杀核心服务连接) - 是否有长事务正在修改这张表(用
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5看TRX_QUERY和TRX_MYSQL_THREAD_ID)
如果发现 TRX_QUERY 是空但 TRX_STARTED 很早,基本就是“事务开着没提交”,这种必须 KILL CONNECTION,仅 KILL QUERY 无效。
MySQL 5.7+ 中 Online DDL 为何有时仍卡住
ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE 看似不锁表,但依然需要获取 MDL 锁——只是持有时间极短。一旦有长事务或未提交查询占着 MDL,它照样会卡在“Waiting for table metadata lock”。
常见误区:
-
ALGORITHM=INPLACE≠ 不需要 MDL 锁(它需要,只是不阻塞 DML) -
LOCK=NONE≠ 不等待锁(它仍需等待已有 MDL 锁释放) - 即使只改字段长度(如
MODIFY COLUMN name VARCHAR(255)),在 MySQL 8.0.12 之前仍可能触发全表重建,进而加剧锁竞争
验证方式:执行 DDL 前先跑 SELECT COUNT(*) FROM information_schema.INNODB_TRX,确保结果为 0 或只有瞬时事务。
预防 MDL 锁卡死的关键配置和习惯
靠事后 KILL 是被动防御;真正减少卡死,得从源头控制事务粒度和监控覆盖:
- 在应用侧强制设置
autocommit=1,避免隐式开启长事务 - 数据库端配置
wait_timeout和interactive_timeout(建议 ≤ 300),让空闲连接自动断开 - 上线 DDL 前,用
SELECT * FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%your_table_name%'扫一遍活跃语句 - 对大表 DDL,加
pt-online-schema-change或gh-ost做灰度变更,绕过原生 MDL 机制
最容易被忽略的一点:MDL 锁不会因为客户端断连而立即释放——MySQL 需要检测到连接异常并完成清理,这个过程可能长达 net_read_timeout(默认 30 秒)。所以网络不稳定时,看似“断开了”,锁还在挂着。











