waiting for table metadata lock 的根源是元数据锁(mdl)被长期持有,需通过 performance_schema.metadata_locks 查找 lock_status='granted' 的持锁线程,尤其关注 sleep 状态但 trx_state='running' 的悬挂事务,kill query 或 kill 对应 processlist_id 以释放锁。

Waiting for table metadata lock 不是锁表本身出了问题,而是有会话攥着元数据锁(MDL)不放,导致后续所有对同一张表的访问(包括 SELECT)全部排队。它和 InnoDB 行锁无关,innodb_lock_wait_timeout 完全无效,必须从 MDL 持有者入手。
查 performance_schema.metadata_locks 找真正在持锁的线程
别只看 SHOW PROCESSLIST —— 它根本不会显示谁在持有 MDL 锁,只显示“谁在等”。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' AND LOCK_STATUS = 'GRANTED';
- 重点关注
LOCK_TYPE是SHARED_READ或SHARED_WRITE的行,对应PROCESSLIST_ID就是持锁者
识别“Sleep 却 RUNNING”的悬挂事务
最常见的持锁者,就是 Command = 'Sleep'、Info 为空、但 Time 已几百秒的连接。它往往对应一个显式开启却未提交的事务:
- 执行过
BEGIN或START TRANSACTION,只做了个SELECT就挂了 - 应用用了连接池但没正确关闭事务(比如 Python 忘了
conn.commit(),Java 的Connection.close()没触发事务结束) - DBA 在客户端敲完
BEGIN就去开会了,事务一直开着 - 验证方法:用上一步拿到的
PROCESSLIST_ID查information_schema.INNODB_TRX,确认trx_state = 'RUNNING'且trx_started时间很早
警惕那些“只读却危险”的操作
不是只有 UPDATE 或 INSERT 才会持锁,这些看似无害的操作同样会悄悄拿走 MDL 并长期不放:
-
SELECT在REPEATABLE READ隔离级别下,第一次执行就会对表加SHARED_READMDL 锁,直到事务结束 -
mysqldump --single-transaction会为所有 dump 表加SHARED_READ锁,dump 没结束,锁就不放 - 查询
INFORMATION_SCHEMA.TABLES等系统表,也可能触发短暂但关键的 MDL 请求,高并发下易成瓶颈 - 触发器里隐式访问的其他表(如
AFTER UPDATE触发器里查了logs表),会让主 DML 实际需要多张表的 MDL 锁,任意一张被 DDL 占着,整条语句就卡住
KILL 前先 KILL QUERY,避免误伤长事务
定位到持锁线程后,别急着 KILL 整个连接,优先尝试 KILL QUERY:
-
KILL QUERY <processlist_id></processlist_id>只中断当前 SQL,事务仍保持打开,MDL 锁可能还在 —— 这适用于你只想中止一个卡住的SELECT,但想保留业务逻辑完整性 -
KILL <processlist_id></processlist_id>会强制回滚整个事务,释放所有 MDL 锁 —— 这是真正解堵的手段,但要注意是否影响上游业务一致性 - 特别注意:如果持锁者是
mysqldump或备份工具,KILL可能导致备份失败或临时文件残留,需同步清理
真正难处理的从来不是那个显示 Waiting for table metadata lock 的线程,而是那个 Sleep 着、Info 为空、Time 却已上千秒、还稳稳 GRANTED 着 SHARED_READ 锁的“幽灵连接”——它不报错、不超时、不写日志,只安静地把整张表钉死在元数据层。











