waiting for table metadata lock是mdl锁冲突所致,根本原因是ddl操作需独占元数据锁,但被长事务、未提交事务或慢查询持有的共享锁阻塞;须通过performance_schema定位阻塞源并终止持锁事务。

“Waiting for table metadata lock” 不是连接数超了,而是 DDL 和 DML 在抢同一张表的元数据锁,长连接只是放大了这个问题。
为什么会出现 Waiting for table metadata lock
MySQL 的 METADATA LOCK(MDL)在执行 DDL(如 ALTER TABLE、DROP INDEX)时自动加锁,且是**强一致性锁**:它会阻塞后续所有对该表的 DML(SELECT/UPDATE/INSERT)和 DDL,直到当前 DDL 完成。而如果此时有一个长事务或未提交事务正在访问该表(哪怕只是 SELECT),MDL 就会卡住——因为 DDL 必须等所有已有事务释放对表的 MDL 共享锁后,才能拿到独占锁。
常见诱因包括:
- 应用端开启事务后执行了
SELECT,但忘记COMMIT或ROLLBACK,连接保持ACTIVE状态并持有 MDL 共享锁 - 慢查询(尤其没走索引的全表扫描)长时间占用连接,导致后续 DDL 排队等待
- 监控脚本或运维工具用
SELECT * FROM information_schema.tables等语句频繁访问系统表,触发隐式 MDL - 使用
mysqldump --single-transaction备份时,若备份期间有长事务存在,也可能拖慢 MDL 释放
如何快速定位正在阻塞的会话
别只看 SHOW PROCESSLIST ——它只显示当前命令,不体现锁等待链。必须查 performance_schema:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_pid, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_pid, b.trx_query blocking_query FROM performance_schema.threads t JOIN information_schema.innodb_trx r ON t.thread_id = r.trx_mysql_thread_id JOIN information_schema.innodb_trx b ON b.trx_id = r.trx_wait_started WHERE r.trx_state = 'LOCK WAIT';
如果 performance_schema 未启用(默认可能关闭),先执行:
SET GLOBAL performance_schema = ON;
更轻量的替代方式(适用于紧急排查):
SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE state = 'Waiting for table metadata lock' ORDER BY time DESC;
然后对每个结果中的 id,再查它的前一个活跃操作:
SELECT * FROM information_schema.processlist WHERE id = <code>blocking_pid</code>;
长连接不是原罪,但会让问题更难察觉
长连接本身没问题,问题在于它**长期持有事务或未释放资源**。PHP 的 PDO::ATTR_PERSISTENT、Java 的 HikariCP 默认连接超时(connection-timeout)设为 30 秒,但若应用逻辑里开了事务却没关,这个连接就变成“幽灵事务持有者”。
关键控制点:
-
wait_timeout和interactive_timeout控制空闲连接断开时间,但它们**对已开启事务的连接无效**——事务期间连接不会被超时中断 - 务必在应用层强制事务边界:用
try/finally或using确保COMMIT/ROLLBACK执行,哪怕发生异常 - 避免在事务中做 HTTP 调用、文件读写等外部耗时操作——这会让事务悬停,MDL 锁死时间拉长
- 对管理类操作(如备份、统计脚本),尽量避开业务高峰,并加
SET SESSION lock_wait_timeout = 10防止无限等待
DDL 操作本身也要优化
MySQL 5.6+ 支持部分 ALTER TABLE 的 online DDL,但并非全部。例如:
-
ADD COLUMN在大多数情况下支持ALGORITHM=INPLACE,不锁表 -
ADD INDEX支持ALGORITHM=INPLACE,但会阻塞并发 DML(非阻塞 SELECT) -
MODIFY COLUMN类型变更(如VARCHAR(255) → VARCHAR(500))通常需要COPY,全程锁表
执行前务必确认:
ALTER TABLE t1 ADD COLUMN c1 INT, ALGORITHM=INPLACE, LOCK=NONE;
如果报错 LOCK=NONE is not supported,说明当前操作无法完全无锁,需评估业务窗口期;不要强行加 LOCK=SHARED 就以为安全——它仍会阻塞写入。
真正容易被忽略的是:**即使你用了 online DDL,只要过程中有长事务在读这张表,MDL 等待照样发生**。所以优化重点永远是“缩短事务生命周期”,而不是只盯着 DDL 语法。











