MDL是Server层自动加的元数据锁,保护表结构不被并发修改;Data Lock是引擎层事务内加的数据行/页锁,控制数据访问。二者作用域、触发时机、排查方式均不同,需分开理解与处理。

Metadata Lock(MDL)和Data Lock(数据锁)根本不是同一类锁,不能靠“区分”来用,而必须按作用域、触发时机、排查方式三者分开理解与处理。
MDL 是访问表结构时自动加的锁,不是你写的 SQL 加的
MDL 由 MySQL Server 层在语句执行前自动加,只要访问了某张表(哪怕只是 SELECT),就会持有该表的 MDL 读锁;执行 ALTER TABLE、DROP TABLE 等 DDL 时则需获取 MDL 写锁。它不依赖存储引擎,MyISAM 和 InnoDB 都有。
- MDL 锁住的是“表的元信息”,比如表是否存在、字段类型、索引定义等,跟行数据无关
- 你无法用
LOCK TABLES或事务显式控制 MDL,也无法在事务里“释放”它——只有语句执行完或连接断开才释放 - 常见阻塞现象:
ALTER TABLE t1 ADD COLUMN x INT卡住,查performance_schema.metadata_locks会看到它在等一个SHARED_READ锁被释放,源头往往是一个长事务里的SELECT * FROM t1
Data Lock 是引擎层对数据行/页/表的实际操作锁,受事务生命周期约束
Data Lock 包括 InnoDB 的行锁(RECORD LOCK)、间隙锁(GAP LOCK)、临键锁(NEXT-KEY LOCK),以及表级数据锁(如 LOCK TABLES t1 WRITE)。它们只在事务内生效,且随事务提交或回滚释放。
- 行锁是否生效,取决于语句是否命中索引:全表扫描可能升级为表锁;
WHERE id = ?走主键则只锁对应行 -
SELECT ... FOR UPDATE或UPDATE会加 Data Lock,但不会触发 MDL 写锁;而ALTER TABLE会等所有相关表的 MDL 读锁释放,不管有没有 Data Lock - 查当前 Data Lock 状态得看
information_schema.INNODB_TRX+INNODB_LOCKS(MySQL 5.7)或performance_schema.data_locks(MySQL 8.0+)
两者同时存在时,谁卡住谁?看锁等待链
典型死锁场景不是两个 Data Lock 互等,而是 MDL 写锁被 Data Lock 持有的事务“间接拖住”。比如:
- Session A:开启事务,执行
SELECT * FROM users WHERE id = 100(持有 users 表的 MDL 读锁 + id=100 的行锁) - Session B:执行
ALTER TABLE users ADD COLUMN phone VARCHAR(20)(需要 users 表的 MDL 写锁 → 等待 Session A 的 MDL 读锁) - Session C:执行
UPDATE users SET name='x' WHERE id = 100(需要行锁 → 等待 Session A 提交)
此时 Session B 并不直接等 Session C,但它被 Session A 的 MDL 读锁卡住;而 Session A 不提交,Session C 就一直挂起 —— 这就是 MDL 和 Data Lock 在时间维度上的耦合。排查必须连着看 performance_schema.metadata_locks 和 data_locks,缺一不可。
最容易被忽略的点:MDL 不释放 ≠ 你在写慢查询
很多人以为“没写 UPDATE/DELETE 就没锁”,但只要有一个未提交事务里执行过任意一条涉及某张表的语句(哪怕只是 SELECT 1 FROM t),这张表的 MDL 读锁就一直挂着。尤其在 ORM 自动开启事务又忘记 commit 的场景下,MDL 会静默阻塞后续 DDL 数小时。这不是 bug,是设计使然 —— MySQL 必须保证 DDL 执行时表结构不被并发修改,所以连最轻量的读访问也要纳入管控。


















