waiting for table metadata lock 并非表被锁死,而是某连接持有元数据锁(mdl)未释放,需通过 performance_schema.metadata_locks 查 lock_status='granted' 的持锁线程,再联合 innodb_trx 与 processlist 定位 command='sleep' 且 trx_state='running' 的悬挂事务,优先 kill query 再考虑 kill 连接。

Waiting for table metadata lock 不是表被锁死了,而是某个连接正拿着元数据锁(MDL)不放——它卡住的不只是你的 ALTER TABLE ... ADD COLUMN,还会让后续所有对这张表的读写(包括 SELECT)全部排队等待,最终拖垮整个服务。
真正要杀的,从来不是那个显示 Waiting for table metadata lock 的线程,而是背后那个“安静”持锁却没提交的事务。
查 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。再查具体锁(把 your_db 和 your_table 替换为实际值):
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST<br>FROM performance_schema.metadata_locks m<br>JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID<br>WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
重点关注 LOCK_STATUS = 'GRANTED' 的行,尤其是 LOCK_TYPE 为 SHARED_READ 或 SHARED_WRITE 的连接。
联合 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<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;
-
trx_started时间越早,挂得越久,优先处理 - 典型特征:
COMMAND = 'Sleep'、TIME > 300、INFO为空、trx_isolation_level = 'REPEATABLE READ' - 这类连接通常没实际数据操作(
SHOW ENGINE INNODB STATUS\G中tables in use和locked tables均为 0),纯属空转占锁
KILL QUERY 优先于 KILL 连接
直接 KILL 连接会触发完整事务回滚,可能耗时数分钟甚至更久,期间仍持有 MDL 锁,雪崩继续。
正确顺序是:
- 先试
KILL QUERY <processlist_id></processlist_id>:只中断当前语句,不终止连接,事务仍存在但不再执行新语句 - 若无响应或仍卡住,再
KILL <processlist_id></processlist_id> - 特别注意:不要 KILL 那个显示
Waiting for table metadata lock的线程——它只是等待者,不是阻塞源
为什么查不到阻塞源?重点看 events_statements_current
如果 metadata_locks 和 INNODB_TRX 都查不到活跃事务,但锁依然存在,大概率是显式事务中执行了失败语句(如 SELECT nonexist_col FROM your_table),锁未释放但事务未真正开启。
查失败语句:
SELECT THREAD_ID, SQL_TEXT, STATEMENT_NAME, TIMER_WAIT<br>FROM performance_schema.events_statements_current<br>WHERE SQL_TEXT LIKE '%your_table%' AND (SQL_TEXT NOT LIKE '%SELECT%' OR SQL_TEXT LIKE '%error%');
找到对应 THREAD_ID 后,KILL QUERY 即可释放锁。
这种场景最容易被忽略:连接池归还了连接,但应用层没 COMMIT 或 ROLLBACK,事务卡在中间态,既不在 PROCESSLIST 显眼位置,也不在 INNODB_TRX 的活跃列表里——它就静静躺在 metadata_locks 里,等着拖垮你下一次 DDL。











