直接查sys.schema_table_lock_waits定位blocking_pid持锁者,而非kill waiting_pid;若返回为空,需确认performance_schema启用及mdl采集器开启,blocking_pid为null时可能为隐式事务悬挂持锁。

直接看 SHOW PROCESSLIST 里大量线程状态为 Waiting for table metadata lock,基本就是 MDL 阻塞,不是行锁或死锁问题。
查谁在等、谁在堵:用 sys.schema_table_lock_waits
这个视图是 MySQL 5.7+ 自带的“阻塞关系快照”,比手动 JOIN 更直接:
- 执行
SELECT * FROM sys.schema_table_lock_waits\G,重点关注blocking_pid(真凶)和waiting_pid(受害者) - 如果返回为空,先确认
performance_schema已启用:SELECT @@performance_schema;应为1 - 再检查采集器是否打开:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';,ENABLED必须是YES -
blocking_pid为NULL不代表没锁,很可能是隐式事务(比如BEGIN后只跑了一条SELECT就断开)在持锁
找“Sleep 却 RUNNING”的悬挂事务
最常卡住 DDL 的不是正在跑 SQL 的线程,而是 COMMAND = 'Sleep'、STATE 为空、但事务仍在运行的连接:
- 执行 JOIN 查询:
SELECT t.trx_id, t.trx_started, t.trx_state, p.ID, p.USER, p.HOST, p.COMMAND, p.TIME, 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'→ 从BEGIN开始就持有MDL_SHARED_READ锁,哪怕只执行过一次SELECT - 别只看
trx_mysql_thread_id,要结合p.HOST和p.USER定位到具体应用实例或 DBA 终端
确认锁类型和持有者线程:查 performance_schema.metadata_locks
这是唯一能直接看到锁归属的底层视图,SHOW PROCESSLIST 根本不显示它:
- 查持锁者:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, PROCESSLIST_ID 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' AND LOCK_STATUS = 'GRANTED'; -
LOCK_TYPE = 'SHARED_READ'→ 很可能是一条未提交的SELECT或mysqldump -
LOCK_DURATION = 'TRANSACTION'→ 锁会持续到事务结束,不是语句级释放 - 注意:
KILL前务必用SHOW ENGINE INNODB STATUS\G搜索对应trx_id,确认该事务没有正在写 binlog 或修改大量行(避免误杀引发长回滚)
真正容易被忽略的是:MDL 锁对 MyISAM 表同样生效,哪怕表不支持事务;还有,autocommit = 1 下的显式 BEGIN 也会立刻持锁——很多应用在连接池里开了事务却忘了关,锁就一直挂着。











