结论:阻塞元数据锁的通常是处于Sleep状态但事务仍运行的悬挂连接,需通过sys.schema_table_lock_waits或performance_schema.metadata_locks定位并KILL;务必先验证performance_schema及MDL采集器已启用。

直接说结论:出现大量 Waiting for table metadata lock,90% 不是你的 ALTER 或 SELECT 有问题,而是有连接“睡着了却还在持锁”——必须立刻定位并终止那个 COMMAND = 'Sleep' 但 trx_state = 'RUNNING' 的悬挂事务。
查 sys.schema_table_lock_waits 快速看谁堵谁
这是 MySQL 5.7+ 最省力的入口,它把阻塞链直接打平成表格:
-
blocking_pid是真正在 hold 锁的线程 ID,waiting_pid是被卡住的受害者 - 如果返回空,先确认
performance_schema已启用:SELECT @@performance_schema;必须为1 - 再检查 MDL 采集器是否打开:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';,ENABLED和TIMED都得是YES - 若
blocking_pid为NULL,别以为没锁——大概率是隐式事务(比如BEGIN后只跑了一条SELECT就断开)在持MDL_SHARED_READ
用 performance_schema.metadata_locks 看真实持锁状态
SHOW PROCESSLIST 根本不显示谁在 hold MDL 锁,因为它是服务层锁,不是 InnoDB 层的行锁。必须靠这个表:
- 先确保采集器已开(同上),再执行:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED' AND OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table'; - 重点盯
LOCK_TYPE IN ('SHARED_READ', 'SHARED_WRITE')且PROCESSLIST_ID对应的连接 - 去
information_schema.PROCESSLIST查这个 ID:如果COMMAND = 'Sleep'、TIME > 300、INFO为空 → 90% 是连接池归还了连接但没COMMIT
联合 INNODB_TRX 和 PROCESSLIST 找“Sleep 却 RUNNING”的悬挂事务
单查 INNODB_TRX 会漏掉已断连但事务未清理的连接;单看 PROCESSLIST 又容易把真空闲连接当嫌疑对象。必须 JOIN:
- 执行:
SELECT t.trx_id, t.trx_started, t.trx_state, p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.STATE, 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; -
trx_started时间越早越可疑;trx_isolation_level = 'REPEATABLE READ'时,哪怕只执行过一条SELECT,事务一启就持 MDL 锁 -
INFO为空 +TIME > 600→ 基本可判定是应用异常中断或忘记COMMIT - KILL 前务必用
SHOW ENGINE INNODB STATUS\G搜索该trx_id,确认mysql tables in use和locked tables都为 0,再执行KILL QUERY {ID}(优先于KILL {ID})
真正危险的不是那个满屏刷 Waiting for table metadata lock 的线程,而是那个看起来安静、STATE 为空、INFO 为空、TIME 却长达几千秒的 Sleep 连接——它可能从昨天凌晨就挂着没释放 MDL,而你一直没注意到。


















