Waiting for table metadata lock 的真正原因是其他连接持有元数据锁(MDL),需通过 performance_schema.metadata_locks 查找 LOCK_STATUS='GRANTED' 的持锁线程,结合 INNODB_TRX 与 PROCESSLIST 定位“Sleep 但 RUNNING”的悬挂事务,确认后优先 KILL QUERY 再考虑 KILL 连接。

Waiting for table metadata lock 不是表被锁死,而是某个连接正拿着元数据锁(MDL)不放,导致你的 ALTER TABLE、DROP VIEW、TRUNCATE TABLE 等操作卡在等待状态。真正要查的,从来不是那个显示“Waiting”的线程,而是背后那个安静持锁的事务。
查 performance_schema.metadata_locks 定位真正在持锁的线程
MySQL 5.7+ 的 SHOW PROCESSLIST 根本看不到谁在 hold MDL 锁——它只反映执行状态,不体现锁持有关系。必须靠 performance_schema.metadata_locks。
先确认采集已启用:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';
确保 ENABLED 和 TIMED 都是 YES。再查具体锁:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS,
PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST
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';- 重点看
LOCK_STATUS = 'GRANTED'的行,尤其是LOCK_TYPE为SHARED_READ或SHARED_WRITE的连接 - 如果对应
PROCESSLIST_COMMAND = 'Sleep'且PROCESSLIST_TIME > 300,基本就是悬挂事务 - 注意:
PROCESSLIST_INFO为空 +Time很大,是连接池归还连接但没COMMIT的典型特征
联合 INNODB_TRX 和 threads 找出“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,从BEGIN就持有了MDL_SHARED_READ,会阻塞所有 DDL - Python/Java 应用若用连接池但没显式
conn.close()或cursor.close(),极易出现这种“空转占锁”
KILL 前务必用 SHOW ENGINE INNODB STATUS 确认是否真在空转
别一看到线程 ID 就 KILL。有些线程虽然 STATE 是 Sleep,但可能刚完成大事务正写 binlog,或正在做崩溃恢复前的清理。
安全确认步骤:
- 从上一步查出的
trx_id,在SHOW ENGINE INNODB STATUS\G输出中搜索对应事务段 - 看
mysql tables in use和locked tables是否都为0:是 → 基本没在干活,可KILL - 优先试
KILL QUERY [id],只中断当前语句;若无效再KILL [id],终止整个连接 - 避免直接
KILL正在执行ALTER TABLE的线程——它只是受害者,杀完立刻有下一个顶上
为什么不能依赖 sys.schema_table_lock_waits?
这个视图确实能直接告诉你“谁在等、谁在堵”,但它依赖 sys 库已启用且权限完整。生产环境常因权限限制或 sys 未初始化而查不到数据。
更可靠的做法是绕过 sys,直查底层:
SELECT m.OBJECT_SCHEMA, m.OBJECT_NAME, m.LOCK_TYPE, m.LOCK_STATUS,
t.PROCESSLIST_ID, t.PROCESSLIST_USER, t.PROCESSLIST_HOST,
t.PROCESSLIST_COMMAND, t.PROCESSLIST_TIME
FROM performance_schema.metadata_locks m
JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID
WHERE m.LOCK_STATUS IN ('PENDING', 'GRANTED');-
LOCK_STATUS = 'PENDING'的是等待方(受害者) -
LOCK_STATUS = 'GRANTED'的是持锁方(元凶) - 注意:
autocommit = 0的连接,哪怕只执行过一条SELECT,也会从第一条语句起就持有 MDL 读锁,直到COMMIT或连接断开
最易被忽略的一点:MDL 锁对 MyISAM 表同样生效。不要因为表引擎是 MyISAM 就排除事务干扰——只要连接设了 autocommit = 0 并执行过任意语句,就会持锁。


















