mdl锁生命周期严格绑定事务,必须提交或回滚事务才能释放;其设计保障可重复读隔离级别下元数据视图稳定,防止ddl干扰dml,kill query无效因不终结事务,唯一解法是终止持锁连接或促使其事务结束。

MDL锁的生命周期绑定在事务上
MySQL 8.0中,MDL锁不是“随语句结束就释放”,而是严格绑定到事务生命周期——只要事务没提交或回滚,哪怕只执行了一条SELECT,该会话对涉及表持有的MDL_SHARED_READ锁就不会释放。这是5.5.3引入MDL后确立的核心行为,8.0只是延续并强化了这一设计。
为什么不能按语句释放MDL锁?
按语句释放会破坏可重复读(RR)隔离级别语义。比如事务内两次SELECT * FROM t之间,若中间允许其他会话执行ALTER TABLE t DROP COLUMN x,第二次查询就会因列缺失报错,或返回不一致结构——这直接违背RR“同一事务中多次读取结果一致”的承诺。
- RR隔离级别下,
MDL_SHARED_READ锁必须持有到事务结束,才能保证元数据视图稳定 -
MDL_SHARED_WRITE(如INSERT/UPDATE/DELETE)同样绑定事务,否则DML中途DDL介入会导致binlog顺序错乱 - 即使
autocommit=1,单条语句也构成隐式事务,所以MDL锁仍会在语句执行完自动提交后释放
容易被忽略的“伪长事务”陷阱
开发常误以为“没写BEGIN就没事务”,但以下情况都会让MDL锁意外长期持有:
- 显式开启事务后忘记
COMMIT或ROLLBACK,连接空闲但锁仍在 - 应用使用连接池,未正确关闭事务就归还连接,导致锁滞留数小时
-
SELECT ... FOR UPDATE后未提交,不仅持行锁,也继续持MDL_SHARED_WRITE - 存储过程中有异常未处理,
EXIT HANDLER缺失,事务卡在中间状态
这类问题不会立刻报错,但会阻塞后续ALTER TABLE,现象是show processlist里出现waiting for table metadata lock,而源头事务可能早已“静默挂起”。
如何验证MDL锁是否已释放?
别依赖SHOW PROCESSLIST——它只显示等待中的会话。真正要看锁状态,得查performance_schema.metadata_locks:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED';
注意:LOCK_DURATION为TRANSACTION表示该锁属于事务级,必然随事务结束释放;若看到STATEMENT,说明是极早期版本或特殊场景(如FLUSH TABLES),8.0默认全是TRANSACTION。
真正的复杂点在于:MDL锁释放本身不可干预,你只能控制事务——要么让它快点结束,要么不让它开始。任何想“手动释放MDL”的尝试(比如KILL会话)本质都是终止事务,而非解锁。











