查sys.schema_table_lock_waits可直接定位mdl阻塞链:blocking_pid为持有锁的连接id,waiting_pid为被卡ddl,结合performance_schema.metadata_locks可确认lock_type与持有者线程,需检查事务状态而非仅看processlist。

查 sys.schema_table_lock_waits 快速定位谁在堵谁
看到 Waiting for table metadata lock 状态,别急着翻 SHOW PROCESSLIST —— 它只告诉你“卡了”,不告诉你“为什么卡”。sys.schema_table_lock_waits 是 MySQL 5.7+ 自带的诊断视图,直接暴露阻塞链:blocking_pid 是持有锁的连接 ID,waiting_pid 是被卡住的 DDL 或查询,sql_text 字段还能看到持有者正在执行(或挂起)的语句。
执行这句就行:
SELECT * FROM sys.schema_table_lock_waits\G
注意几点:
- 如果返回为空,先确认
performance_schema已启用(SELECT @@performance_schema;应为 1),且wait/lock/metadata/sql/mdl采集器已开(UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';) -
blocking_pid为NULL不代表没锁,很可能是隐式事务(比如BEGIN后只跑了一条SELECT就断开)在持锁 - 该视图不显示
Sleep状态的空闲连接,所以若阻塞源是“挂着但没操作”的连接,得往下看
联合 INNODB_TRX 和 PROCESSLIST 找出“Sleep 却 RUNNING”的悬挂事务
很多元数据锁问题不是来自活跃 SQL,而是来自应用连接池归还了连接但没 COMMIT。这种连接在 PROCESSLIST 里是 Command = 'Sleep',但在 INNODB_TRX 里仍是 trx_state = 'RUNNING' —— 它从 BEGIN 开始就一直拿着 MDL_SHARED_READ 锁不放。
必须用 JOIN 查,单查 INNODB_TRX 会漏掉:
SELECT t.trx_id, t.trx_started, t.trx_state, t.trx_isolation_level,<br> p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.STATE, p.INFO<br>FROM information_schema.INNODB_TRX t<br>JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID<br>WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING'<br>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 终端
用 performance_schema.metadata_locks 看锁类型和持有者线程
sys.schema_table_lock_waits 有时字段不够细,比如想确认锁类型是 SHARED_READ 还是 EXCLUSIVE,或者需要拿到持有者线程的完整客户端 IP 和堆栈,就得直查底层表。
先确保采集器已开:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
再查锁详情:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS,<br> OWNER_THREAD_ID, OWNER_EVENT_ID<br>FROM performance_schema.metadata_locks<br>WHERE LOCK_STATUS = 'GRANTED';
重点看:
-
LOCK_TYPE:常见SHARED_READ(普通查询)、EXCLUSIVE(DDL);两者互斥 -
OWNER_THREAD_ID:对应performance_schema.threads.THREAD_ID,可进一步查PROCESSLIST_USER、PROCESSLIST_HOST -
LOCK_DURATION = 'TRANSACTION'→ 锁生命周期绑定事务,不是语句级的,哪怕只跑一条SELECT也得等事务结束才释放
KILL 前务必用 SHOW ENGINE INNODB STATUS\G 验证是否真可杀
看到一个 TIME > 600 的 Sleep 连接,别手抖直接 KILL。它可能正处在事务中间状态,贸然终止可能触发回滚风暴或留下半截事务。
先查它的 InnoDB 状态:
SHOW ENGINE INNODB STATUS\G
在 TRANSACTIONS 部分找对应 trx_id,看这两项:
-
mysql tables in use和locked tables都为 0 → 没实际表操作,基本可安全KILL -
STATE是committing或rolling back→ 正在收尾,等它自己完成 -
STATE是starting authentication或Waiting for table metadata lock→ 很可能已僵死,优先KILL QUERY中断当前语句,再观察
真正容易被忽略的是:MySQL 8.0.29+ 对 MDL 等待超时更激进,但锁本身不会自动释放——只要事务没提交或连接没断,SHARED_READ 锁就一直占着,DDL 就永远卡在那儿。排查必须穿透到事务生命周期,不能只盯 SQL 执行时间。











