SHOW PROCESSLIST无法定位MDL锁持有者,因持锁线程常处Sleep状态且State为空;必须查performance_schema.metadata_locks,结合threads和events_statements_current三表关联,才能准确定位持锁会话。

查 performance_schema.metadata_locks 定位持有者
直接看 SHOW PROCESSLIST 通常找不到元凶,因为真正持锁的会话可能只执行了一条 SELECT 就没提交,State 显示为 Sleep,查询内容为空。必须依赖 performance_schema.metadata_locks ——它能暴露“谁拿了锁”和“谁在等锁”的真实状态。
先确认消费者已启用:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'global_instrumentation';
再执行定位查询:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING' OR LOCK_DURATION = 'TRANSACTION';
-
LOCK_STATUS = 'PENDING'的行是正在卡住的会话(比如 ALTER TABLE) -
LOCK_DURATION = 'TRANSACTION'的行是持有锁的活跃事务,哪怕它当前没在执行 SQL - 把这两类结果按
OBJECT_SCHEMA和OBJECT_NAME对齐,就能锁定阻塞源头
KILL CONNECTION 必须配对 thread_id 和事务状态
KILL QUERY 对元数据锁完全无效,它只终止语句,不结束事务;只有 KILL(即 KILL CONNECTION)才能切断连接、触发事务回滚、释放 MDL。但盲目 KILL 风险很大:
- 如果被杀会话正处在
Rolling back状态,MDL 仍持续持有,其他 DDL 继续卡住 - 若该连接归属主从复制线程或监控采集任务,
KILL可能导致延迟飙升或指标丢失 -
thread_id必须来自performance_schema.threads.PROCESSLIST_ID,不是INNODB_TRX.trx_mysql_thread_id(后者在某些版本下可能滞后)
安全做法是:先查 performance_schema.threads 确认 PROCESSLIST_ID 和 PROCESSLIST_STATE,再对 PROCESSLIST_STATE IN ('Sleep', 'Waiting for table metadata lock') 且 PROCESSLIST_TIME > 300 的连接执行 KILL。
为什么 KILL 后还卡着?别忽略事务回滚延迟
执行 KILL 后立刻查 SHOW PROCESSLIST 看不到对应线程,不代表锁已释放。MySQL 的事务清理不是原子操作:
- 大事务回滚要重放 undo log,可能耗时几十秒甚至几分钟
- 磁盘 I/O 压力高时,
Rolling back状态可能长时间卡在 “cleaning up” 阶段 -
performance_schema.metadata_locks中对应OWNER_THREAD_ID的记录不会立即消失,得等事务真正结束
此时继续查 performance_schema.events_statements_current,如果看到该 THREAD_ID 的 SQL_TEXT 是 ROLLBACK,就只能等——强行重启 MySQL 可能导致页损坏或 binlog 不一致。
ALTER TABLE 卡住时,别只盯着 DDL 本身
遇到 ALTER TABLE 报 Waiting for table metadata lock,第一反应不该是杀 DDL 进程,而是找谁在读这张表。常见陷阱:
- 一个未提交的
SELECT * FROM orders就足以让后续所有 DDL 排队 - 应用层 ORM 自动开启事务但忘记
COMMIT,这种会话在INNODB_TRX中trx_query为NULL,只能靠trx_started时间判断是否陈旧 - 使用了
READ-COMMITTED隔离级别,不代表没 MDL —— MDL 和隔离级别无关,只要事务开着,锁就一直挂着
最稳妥的路径是:用 metadata_locks 找出 LOCK_DURATION = 'TRANSACTION' 的持有者 → 查它的 PROCESSLIST_USER 和 PROCESSLIST_HOST → 联系对应业务方确认是否可中断 → 再决定是否 KILL。
MDL 锁的释放时机不在语句结束那一刻,而在事务生命周期终结之时。很多“卡死”问题本质是事务管理失控,而非锁机制本身异常。排查时盯住事务,而不是语句。


















