4. 元数据锁(Metadata Lock)基础
元数据锁(Metadata Lock,简称 MDL)是 MySQL 内部用于保护数据库对象(表、视图、存储过程等)元数据一致性的锁机制。在 DDL 与 DML 并发执行时,MDL 是保证元数据不出现逻辑损坏的核心防线。理解 MDL 的类型与行为,是排查 Liquibase 迁移过程中因锁等待导致 GC 停顿或连接池耗尽的前提。
4.1 什么是 MDL
MDL 不是表锁或行锁,它锁的是对象的定义。当会话 A 正在执行一条 SELECT 时,MySQL 会在该表上获取一个 MDL 共享锁,防止其他会话在此期间修改表结构。如果会话 B 尝试执行 ALTER TABLE,它需要获取 MDL 排他锁,此时必须等待会话 A 释放共享锁。
关键认知: MDL 等待不会直接导致 GC 停顿,但长时间未释放的 MDL 会阻塞后续所有 DDL/DML,导致连接堆积、事务超时,最终触发应用线程阻塞和 JVM 堆内存压力上升,间接引发 GC 停顿。
4.2 MDL 的类型
MySQL 8.0 将 MDL 分为以下主要类型,按锁强度从低到高排列:
| 锁类型 | 缩写 | 强度 | 典型场景 |
|---|---|---|---|
| MDL_SHARED | S | 低 | SELECT、INSERT、UPDATE、DELETE |
| MDL_SHARED_READ | SR | 低 | SELECT … FOR SHARE |
| MDL_SHARED_WRITE | SW | 中 | DML(INSERT/UPDATE/DELETE) |
| MDL_SHARED_UPGRADABLE | SU | 中高 | ALTER TABLE … ALGORITHM=INPLACE(准备阶段) |
| MDL_EXCLUSIVE | X | 高 | ALTER TABLE、DROP TABLE、CREATE INDEX |
排错要点: 在
performance_schema.metadata_locks 表中,LOCK_STATUS 为 PENDING 且 LOCK_TYPE 为 EXCLUSIVE 的记录,通常就是阻塞链的源头。
4.3 MDL 在 DDL/DML 中的行为
MDL 的获取与释放遵循以下规则:
- DML 获取共享锁(S/SW):事务开始时获取,事务提交或回滚后释放。长事务会长时间持有 MDL 共享锁。
- DDL 获取排他锁(X):DDL 语句执行前需要等待所有共享锁释放,执行期间阻塞所有后续 DML。
- 锁升级路径:ALTER TABLE 通常先获取 SU 锁(允许并发 DML),在表重建或元数据提交阶段升级为 X 锁。
- 等待队列:X 锁请求会排在等待队列头部,后续 S 锁请求即使早于 X 锁到达,也会被 X 锁阻塞(写优先)。
-- 查看当前 MDL 等待情况(MySQL 8.0)
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
LOCK_TYPE,
LOCK_STATUS,
OWNER_THREAD_ID,
WAITER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE LOCK_STATUS = 'PENDING';
Liquibase 迁移陷阱: 如果 Liquibase 的 changelog 中包含了多个 DDL 操作,且迁移事务未及时提交,或者应用侧存在长事务读取被迁移的表,就会形成 MDL 等待链。等待链导致连接池线程阻塞,进而引发 JVM 线程饥饿和 GC 压力。
4.4 MDL 等待链与 GC 停顿的关联
下图展示了 MDL 等待如何从数据库层传导至应用层,最终触发 GC 停顿:
4.5 排查 MDL 的常用命令
| 命令/视图 | 用途 | 适用版本 |
|---|---|---|
SHOW PROCESSLIST |
查看处于 Waiting for table metadata lock 状态的线程 |
5.6+ |
performance_schema.metadata_locks |
查看所有 MDL 锁的持有与等待关系 | 8.0+ |
sys.schema_table_lock_waits |
可视化 MDL 等待链(依赖 performance_schema) | 8.0+ |
SELECT * FROM information_schema.INNODB_TRX |
查找长时间未提交的事务 | 5.6+ |
最佳实践: 在 Liquibase 迁移脚本中,始终在 DDL 前执行
SET SESSION lock_wait_timeout = 5,避免迁移线程被 MDL 无限阻塞。同时,监控 performance_schema.metadata_locks 中 PENDING 状态的记录数,作为告警指标。