MySQL 8.0 中快速定位阻塞的MDL锁需查询performance_schema.data_lock_waits获取等待链,并结合metadata_locks、threads和INNODB_TRX三表联合诊断,重点识别持有LOCK_DURATION='TRANSACTION'且PROCESSLIST_TIME>60的休眠长事务。

如何快速定位正在阻塞的MDL锁
MySQL 8.0 中 MDL(Metadata Lock)锁阻塞不会直接报错,但会表现为查询长时间卡在 Waiting for table metadata lock 状态。关键不是等超时,而是立刻查清谁在 hold 锁、谁在等锁。
执行以下查询能直接看到锁等待链:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM performance_schema.data_lock_waits w INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.BLOCKING_TRX_ID INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.REQUESTING_TRX_ID;
注意:performance_schema.data_lock_waits 在 MySQL 8.0 默认启用,但需确认 performance_schema 已开启且相关 consumers 已激活(如 events_statements_current、data_locks)。若查不到结果,先检查:
-
SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE 'global_instrumentation' OR NAME LIKE 'data_locks';—— 确保值为YES -
SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';—— 必须为ON
为什么 ALTER TABLE 会卡住 SELECT,而 SELECT 却不报错
这是 MDL 锁升级机制导致的典型现象:一个长事务中执行了 SELECT(哪怕只是 SELECT * FROM t LIMIT 1),就会在表上加 MDL_SHARED_READ 锁;此时另一个连接尝试 ALTER TABLE t ADD COLUMN x INT,需要获取 MDL_EXCLUSIVE 锁,但该锁与任何已有 MDL 锁互斥,因此被挂起等待。
根本原因不是语句本身慢,而是锁兼容性规则严格。MySQL 8.0 不再允许“跳过”已存在的读锁去执行 DDL —— 这是为保证数据字典一致性做的加固。
常见误判点:
- 认为只有显式事务才持锁 → 实际上自动提交的
SELECT也会在执行期间持有短暂 MDL 锁,但如果语句很快完成,通常观察不到阻塞;真正危险的是未提交事务中的SELECT - 以为
KILL掉等待线程就能解围 → 实际上要KILL的是持有锁的线程(blocking_thread),否则等待队列还在积压 - 依赖
innodb_lock_wait_timeout自动中断 → 它只对 InnoDB 行锁有效,对 MDL 锁完全无效
如何安全执行高频 DDL 而不引发雪崩式阻塞
核心思路是「缩短锁持有时间」+「避开活跃窗口」+「主动控制锁粒度」,而非依赖重试或调大超时。
实操建议:
- 用
pt-online-schema-change或gh-ost替代原生ALTER TABLE,它们通过触发器/影子表绕开 MDL_EXCLUSIVE 需求,但要注意:MySQL 8.0.29+ 对触发器元数据加锁更严,需确认工具版本兼容性 - 禁止在业务高峰执行 DDL;上线前用
SELECT COUNT(*) FROM performance_schema.threads WHERE TYPE = 'FOREGROUND' AND PROCESSLIST_STATE = 'Executing'快速评估当前活跃连接压力 - 对必须在线执行的轻量 DDL(如加字段),可配合
ALTER TABLE ... ALGORITHM=INSTANT(仅限满足条件的变更),它不重写表、不阻塞 DML,但要求表引擎为 InnoDB、无全文索引、非虚拟列等限制 - 设置会话级超时:在 DDL 前执行
SET lock_wait_timeout = 10;,这样如果等不到锁,语句会报错ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded,便于脚本捕获并重试
performance_schema 中哪些表真正反映 MDL 现状
别只盯着 data_lock_waits —— 它只记录“当前正在等待”的关系,一旦锁释放就消失。要持续监控,得组合查三张表:
-
performance_schema.metadata_locks:实时列出所有已持有的 MDL 锁,含OBJECT_TYPE(TABLE / SCHEMA)、LOCK_TYPE(EXCLUSIVE / SHARED_READ / SHARED_WRITE 等)、OWNER_THREAD_ID。重点看LOCK_DURATION = 'TRANSACTION'的锁,这类锁生命周期绑定事务,风险最高 -
performance_schema.threads:结合THREAD_ID查出对应连接的PROCESSLIST_ID、PROCESSLIST_INFO(当前执行语句)、PROCESSLIST_TIME(已运行秒数),快速识别长事务 -
information_schema.INNODB_TRX:补充事务开始时间、事务状态、是否在 sleep,和前两张表JOIN后能准确定位“谁拿着锁不放”
一个典型诊断 SQL:
SELECT m.OBJECT_SCHEMA, m.OBJECT_NAME, m.LOCK_TYPE, m.LOCK_DURATION, t.PROCESSLIST_ID, t.PROCESSLIST_INFO, t.PROCESSLIST_TIME FROM performance_schema.metadata_locks m JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID WHERE m.LOCK_STATUS = 'GRANTED' AND m.OBJECT_TYPE = 'TABLE' AND t.PROCESSLIST_STATE = 'Sleep' AND t.PROCESSLIST_TIME > 60;
这条语句专门揪出“休眠中却还霸着表锁超过 1 分钟”的连接——这几乎肯定是开发忘提交事务或应用连接池配置异常导致的。
MDL 锁的问题从来不在“怎么加”,而在“什么时候放”。MySQL 8.0 把元数据一致性提到更高优先级,意味着 DBA 必须更早介入应用层连接生命周期管理,而不是只盯着慢查询日志。


















