查 Waiting for table metadata lock 的源头是持有S MDL锁的长事务,需用sys.schema_table_lock_waits或performance_schema.metadata_locks定位等待关系,并通过information_schema.innodb_trx查找未提交事务,再KILL对应PROCESSLIST_ID;预防关键在autocommit=1、及时提交及DDL前检查活跃事务。

查 Waiting for table metadata lock 是谁在卡DDL
看到这个状态,说明已经有 DDL(比如 ALTER TABLE)被挂起了,它不是源头,而是受害者。真正的问题是某个长事务或未提交的查询正拿着 S MDL 锁不放。
- 立刻执行
SELECT * FROM sys.schema_table_lock_waits;—— 这是 MySQL 5.7+ 自带的视图,直接告诉你谁在等、谁在堵、等了多久、堵的是哪条 SQL - 如果没启用
sys库或权限受限,退而用SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING';找出等待中的锁,再关联OBJECT_SCHEMA和OBJECT_NAME定位表 - 注意:
SHOW PROCESSLIST只能看到“卡住”,看不到“为什么卡”;光看Time列容易误判——有些连接Time很大但实际已空闲,得结合State和Info综合判断
定位持有 S MDL 的长事务(尤其是隐式事务)
绝大多数元数据锁阻塞,根源不在 DDL,而在一个没提交的 SELECT 或其他 DML。它可能来自应用连接池没 close、脚本忘了 COMMIT、甚至只是 DBA 在客户端执行完 BEGIN 就去喝咖啡了。
- 查活跃事务:
SELECT * FROM information_schema.innodb_trx WHERE trx_state = 'RUNNING' AND trx_started —— 关键看 <code>trx_started,别只盯着trx_mysql_thread_id - 反查对应线程正在执行什么:
SELECT THREAD_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_INFO FROM performance_schema.threads t JOIN performance_schema.events_statements_current e USING (THREAD_ID) WHERE t.PROCESSLIST_ID = ?;(把上面查到的trx_mysql_thread_id填进去) - 常见陷阱:Python/Java 应用用了连接池,执行完 SQL 后没显式
cursor.close()或conn.close(),连接被复用但事务没结束,MDL 锁就一直挂着
快速释放锁的实操动作(慎用但有时必须)
确认是某个无业务价值的长事务后,最直接的办法就是 kill 掉它。但别一上来就 KILL,先确认影响范围。
- 先看它有没有在改数据:
SELECT trx_id, trx_state, trx_isolation_level, trx_rows_modified FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = ?;如果trx_rows_modified > 0,说明有未提交的写,kill 会导致回滚,注意评估时间成本 - 安全 kill 命令:
KILL ?;(填PROCESSLIST_ID,不是trx_mysql_thread_id) - 如果连
KILL都卡住(比如线程处于Rolling back),说明回滚本身在争锁,这时只能等——这也是为什么要把lock_wait_timeout设低(如SET GLOBAL lock_wait_timeout = 5;),让后续 DDL 失败得快,别拖垮整个表
预防比排查更重要:几个硬性约束建议
线上环境不能靠每次出事再救火。真正管用的是从机制上减少长 S MDL 存活时间。
- 所有应用连接必须设置
autocommit=1,除非明确需要事务控制;显式BEGIN后,必须配对COMMIT或ROLLBACK,且不能跨函数/HTTP 请求生命周期 - DBA 执行 DDL 前,强制检查:
SELECT COUNT(*) FROM information_schema.innodb_trx WHERE trx_started —— 如果有,暂停操作,先沟通清理 - 禁止在业务高峰期跑
SELECT SLEEP(3600)类测试语句;监控项里加一条:“存在运行超 30 秒的非 DDL 查询”,自动告警
MDL 锁本身不可见、不报错、不超时(默认),它像一层静默的玻璃墙——你撞上去才意识到它存在。最麻烦的不是怎么查,而是那些根本没进 processlist 却还在 hold 锁的连接,它们往往藏在连接池底层或 ORM 的事务管理逻辑里。










