必须终止持有s mdl锁的长事务或隐式会话,否则所有dml/ddl将持续阻塞;通过performance_schema.metadata_locks定位锁持有者,结合information_schema.innodb_trx和threads视图确认事务状态与线程行为,再谨慎kill。

Waiting for table metadata lock 出现时,DDL 操作(如 ALTER TABLE)已卡住,但真正的问题不在它身上——它只是“受害者”。必须立刻定位并终止持有 S MDL 锁的长事务或隐式会话,否则所有对该表的 DML、DDL 都会持续阻塞。
查 performance_schema.metadata_locks 看谁在持锁
SHOW PROCESSLIST 只能看到“卡住了”,看不到“谁在占着不放”。performance_schema.metadata_locks 是唯一能直接看到锁持有者和等待者的视图。
前提是它已启用(MySQL 5.7+ 默认开启,但部分生产环境可能被关闭):
- 先确认是否启用:SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';- 若
ENABLED 为 NO,需执行:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';- 再查具体锁信息(替换
your_db 和 your_table):SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, THREAD_ID, 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' AND LOCK_STATUS = 'GRANTED';- 关键字段含义:
LOCK_TYPE = 'SHARED_READ' 表示是 SELECT 或 mysqldump 持有的读锁;LOCK_DURATION = 'TRANSACTION' 表示该锁会持续到事务结束,不是语句级。
结合 information_schema.innodb_trx 找出未提交事务
持有 S MDL 的往往是一个没COMMIT 或 ROLLBACK 的事务,哪怕只执行了一条 SELECT。
- 查活跃事务:SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx WHERE trx_state = 'RUNNING' ORDER BY trx_started;- 重点看
trx_started 时间戳:如果远早于当前时间(比如几分钟甚至几小时),基本可判定是问题源头
- trx_query 可能为空(例如事务刚 BEGIN 就挂起),此时不能只依赖它判断,要反查线程正在做什么
- 反查线程当前语句(把上面查到的 trx_mysql_thread_id 填入):SELECT PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_DB, PROCESSLIST_INFO FROM performance_schema.threads t JOIN performance_schema.events_statements_current e USING (THREAD_ID) WHERE t.PROCESSLIST_ID = 12345;
警惕那些“看不见”的隐式 MDL 持有者
有些进程不会出现在SHOW PROCESSLIST 的活跃 Query 列里,却长期持有 MDL:
- mysqldump --single-transaction:dump 过程中对所有涉及表加 SHARED_READ 锁,直到 dump 结束;若网络中断或客户端崩溃,锁可能残留
- 应用连接池中的空闲连接:Java/Python 应用开启事务后未显式 commit() 或 close(),连接被复用但事务状态仍为 RUNNING
- information_schema 查询:如 SELECT * FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'xxx',虽执行快,但在高并发下可能堆积并短暂阻塞 DDL
- REPEATABLE READ 隔离级别下的首次 SELECT:事务开启后第一次查某表,就会隐式获取该表的 MDL_READ 锁,且不释放,直到事务结束
KILL 前务必确认影响范围
- 不要一看到trx_started 久就直接 KILL:先用 PROCESSLIST_USER 和 PROCESSLIST_HOST 判断是否为运维脚本、备份任务或低优先级查询
- 如果是 mysqldump 进程,KILL 后 dump 失败,但业务 DDL 可立即恢复;如果是核心业务连接,建议先联系对应团队确认是否可中断
- 执行 KILL 后,锁不会立刻消失,需等事务回滚完成(大事务可能耗时数秒)
- 最小化操作验证:KILL QUERY 12345;(仅终止当前语句,不杀连接)比
KILL 12345;(杀整个连接)更温和,适合试探性处理
真正难的不是找到锁,而是区分“该等”和“不能等”:一个正在跑的报表查询可能合理持有 5 分钟 MDL,而一个卡在中间的 Python 脚本可能已经停了 3 小时却还在挂着事务。时间戳、用户来源、语句内容,三者缺一不可。











