结论是:waiting for table metadata lock 表示有会话正持有mdl锁未释放,需通过performance_schema.metadata_locks定位lock_status='granted'的持锁线程,优先排查command='sleep'、time>300且innodb_trx中trx_state='running'的悬挂事务,避免直接kill引发回滚阻塞。

直接说结论:Waiting for table metadata lock 不是表被锁住了,而是某个会话正拿着元数据锁(MDL)不放,导致你的 ALTER TABLE、DROP VIEW、TRUNCATE TABLE 等操作卡在等待状态。真正要杀的,从来不是那个显示 Waiting for table metadata lock 的线程,而是它背后那个“安静”持有锁的事务。
查不到阻塞源?重点看 performance_schema.metadata_locks
SHOW PROCESSLIST 里看不到谁在 hold 锁,是因为 MDL 是服务层锁,不体现在 State 字段里。MySQL 5.7+ 必须靠 performance_schema.metadata_locks 才能准确定位。
- 先确认该表是否启用:运行
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';,确保ENABLED和TIMED都是YES - 查具体锁关系:把
your_db和your_table替换成实际值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的行——这些就是真正在持锁的连接
Command = 'Sleep' 却卡住 DDL?大概率是未提交事务
最典型的阻塞源,不是正在跑 SELECT 的线程,而是那种 Command = 'Sleep'、State 为空、Time 却高达几百秒的连接。它往往对应一个 BEGIN 了但没 COMMIT 或 ROLLBACK 的事务,MDL 锁从 BEGIN 就已获取,一直不释放。
- 用
SHOW FULL PROCESSLIST;快速筛出可疑连接:关注Command列为Sleep、Time > 300、Info为空的行 - 交叉验证是否真在持锁:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_mysql_thread_id = [thread_id] AND trx_state = 'RUNNING';
再查performance_schema.metadata_locks是否有对应OWNER_THREAD_ID - 这类连接通常没实际数据操作(
SHOW ENGINE INNODB STATUS\G中tables in use和locked tables均为 0),纯属空转占锁
为什么不能直接 KILL?先试 KILL QUERY 再决定
对一个挂起的事务连接执行 KILL [thread_id] 会强制回滚,但风险不小:回滚本身可能耗时数分钟,期间仍阻塞 DDL;还可能触发连接池异常重连或 ORM 框架状态错乱。
- 稳妥做法分两步:
KILL QUERY [thread_id]→ 对Sleep连接基本无效,但可试探是否真有长查询在跑;等 10–20 秒观察状态变化KILL [thread_id]→ 若状态不变,再执行这一步,让事务干净退出 - 特别注意:
mysqldump --single-transaction、SELECT在REPEATABLE READ下首次查询、甚至查INFORMATION_SCHEMA.TABLES都会隐式加MDL_READ锁,别只盯着业务 SQL - 如果数据库版本是 MySQL 8.0+,
performance_schema.metadata_locks默认开启;5.7 需手动打开相关 instruments
真正难处理的,是那些没显式 BEGIN 但 autocommit = 0 的连接——它们执行第一条 SELECT 就自动开启事务并持锁,开发往往意识不到。这种场景下,INNODB_TRX 里能看到 trx_started 时间很早,但 PROCESSLIST_INFO 为空,最容易被忽略。











