Waiting for table metadata lock 根源是未提交事务长期持有MDL锁,需通过performance_schema.metadata_locks定位GRANTED锁持有者,联合INNODB_TRX与PROCESSLIST识别COMMAND='Sleep'但trx_state='RUNNING'的悬挂事务,再KILL终止。

Waiting for table metadata lock 不是你的 ALTER TABLE 写得慢,而是有人拿着锁不放——大概率是个 COMMAND = 'Sleep' 但 trx_state = 'RUNNING' 的悬挂事务,必须立刻定位并终止它。
查 performance_schema.metadata_locks 找真正在持锁的线程
SHOW PROCESSLIST 根本不显示谁在 hold MDL 锁,因为它是服务层锁,不是 InnoDB 行锁。你得靠 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 FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED' AND OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
- 重点盯
LOCK_TYPE IN ('SHARED_READ', 'SHARED_WRITE')的行,对应PROCESSLIST_ID就是持锁者 - 如果返回空,别以为没锁——可能是隐式事务(比如
BEGIN后只跑了一条SELECT就断开),仍在持MDL_SHARED_READ
用 INNODB_TRX + PROCESSLIST 联合确认“Sleep 却 RUNNING”的悬挂事务
单查 INNODB_TRX 会漏掉已断连但事务未清理的连接;单看 PROCESSLIST 又容易把真空闲连接当嫌疑对象。必须 JOIN 关联查。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 执行:
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 FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING';
- 重点关注
TIME > 300、INFO为空、trx_started时间很早的记录 - 这类连接几乎全是连接池归还了连接但没
COMMIT,或应用代码漏了conn.commit()/rollback() - 别急着
KILL,先KILL QUERY <id></id>尝试中断当前语句;若无效再KILL <id></id>
为什么一个 SELECT 就能让整张表瘫痪
SELECT 在 REPEATABLE READ 隔离级别下,第一次执行就会对表加 MDL_SHARED_READ 锁,直到事务结束。这不是 bug,是 MySQL 保证 DDL 安全的强制机制。
-
MDL_SHARED_READ允许多个会话共存,但它和MDL_EXCLUSIVE(如ALTER TABLE)互斥 - 一旦有长事务持着
SHARED_READ,后续所有ALTER、DROP、TRUNCATE都得排队 - 更危险的是:这个排队会“传染”——新来的
SELECT、UPDATE也会被卡在后面,因为它们申请的SHARED_READ或SHARED_WRITE必须等排他锁拿到后才能继续 -
innodb_lock_wait_timeout对它完全无效,这是服务层锁,不是 InnoDB 层的行锁
别信 SHOW OPEN TABLES WHERE IN_USE > 0
这个命令查的是表缓存引用计数,和 MDL 完全无关。它可能显示 IN_USE = 0,但 metadata_locks 里明明有几十个 GRANTED 锁——这是新手最常踩的坑。
-
SHOW OPEN TABLES只反映表是否被打开并缓存在内存中,跟锁持有状态毫无关系 - 真正能反映 MDL 持有关系的,只有
performance_schema.metadata_locks(5.7+)或sys.schema_table_lock_waits(封装版) - 如果你用的是 MySQL 5.6,
metadata_locks表不可用,只能靠INNODB_TRX+PROCESSLIST排查 + 经验判断:找Time特别大、State是Sleep、Info为空的连接
最隐蔽的持锁者往往没有 INFO、不报错、不占 CPU,只是安静地睡在那里,把整张表的元数据锁攥着不松手——排查时盯着 PROCESSLIST_ID 和 trx_state 的组合,比盯着 SQL 本身重要得多。

















