Mysql 中的MDL
本文可以结合 MySQL中的事务原理和锁机制 查看。
首先简单了解一下 mysql 的 sql 类型:
1、数据定义语言 DDL:Create、Drop、Alter 操作。用于定义库和表结构的。
2、数据查询语言 DQL:select。用于查询数据的。
3、数据操纵语言 DML:insert、update、delete。对行记录进行增删改操作。
4、数据控制语言 DCL:grant、revoke、commit、rollback。控制数据库的权限和事务。
MDL
MDL(MetaData Lock)就是针对于 DDL 与 DML、DQL 操作加锁,执行 DDL 自动添加写锁,执行 DML、DQL 自动添加读锁,也就是说 DML 语句可以同时执行(不考虑其他锁),而 DDL 间则会相互阻塞。
为什么在执行 DDL 时会添加写锁?
DDL 在执行会先创建一个临时表,先将表的数据全部移到这个临时表中,然后再将临时表替换当前表。在这个过程中如果出现读写操作就会影响最终结果出错。这个过程耗时主要在将原表的数据移到临时表的过程。
执行
不同事务的执行是按队列顺序进行的,如果两个事务所执行的 MDL 添加的分别是读、写锁,那么就会互斥,后面的事务只有等到前面事务提交释放锁后才能执行。
例子:假设有四个会话:session1、session2、session3、session4 ,首先先依次开启事务,然后session1,session2 先执行查询操作(为了避免可能是可串行化级别导致加的写锁冲突,所以就使用查询操作),session1,session2 中的操作可以正常执行,session3执行 Alter 修改表结构,此时 session3 的操作就会阻塞(因为 MDL 的读写锁冲突),session4 再执行增删改查也会被阻塞,因为是在队列中,它位于 session3 后面,所以只有等到 session3 提交后其才能继续执行。
Online DDL
通过上面的分析,进行实践检验,却出现了下面的情况:
可以看到执行和显示顺序是图中标注的从1开始递增按顺序执行的。可以看到 session4 在 session2 提交后就立刻会执行,并且在 session4 提交后 session3 才能执行成功,这个和前面所说的理论就会冲突。这个原因是当前 mysql 版本是5.6,而在 mysql 5.6 支持 Online DDL。
Online DDL 是对 MDL 的一种优化,因为如果按照 MDL 的执行逻辑,像上面这种情况,一旦一些简单的读写操作比某一条 DDL 语句启动慢一些,就会陷入阻塞,如果 DDL 修改的是大表且是高频表,那么在这条语句执行时会阻塞所有的读写操作,很容易使数据库崩溃。而 Online DDL 优化思路就是在获取到锁之后先允许一段时间的读写操作,直到临时表的数据转移完成,再停止其他读写操作,而具体实现就主要分为下面几步,
1. 拿MDL写锁
2. 降级成MDL读锁,接受读写操作,读操作直接正常返回,写操作会被记录下来,等待后面更新到临时表中。
3. 真正做DDL,在 DDL 表记录向临时表转移完成并执行完记录的写操作后开始阻塞读写操作。
4. 升级成MDL写锁,进行临时表的替换
5. 释放MDL锁
应用于上面的例子就是:
1、session1、session2 因为是队列的前两个,先添加MDL读锁,正常执行,session3 因为是要添加 MDL 写锁所以阻塞,session4 在 session3 后面所以也阻塞,session1、session2执行完成提交释放读锁。
2、session3 在获取到 MDL 写锁后,会先进行降级降级成读锁
3、在 session3 降级后 session4 因为是 MDL 读锁,所以也会获取到锁,执行
4、session3 在完成向临时表数据迁移以及执行完记录的写操作后想升级回写锁发现当前的读锁还被 session4 占用,所以会阻塞,等待 session4 执行释放。
5、session4 提交事务,释放读锁,session3 成功升级锁,输出提示语句,最后提交。
注意:
1、如果前面的例子还有一个 session5,并且在一开始和 session4 一起开启事务、查询,那么在 session2 提交后 session5 也会随着 session4 一起执行,而 session3 则需要等待 session4、session5 提交后才能执行第四步。
而如果 session5 开启事务是在 session3 向临时表数据迁移完成后,那么 session5 的查询操作就会被 session3 阻塞,直到 session3 提交事务。
2、Online DDL 整个过程是在 InnoDB 内部执行的,对于 Server 来说并没有什么操作,所以这个操作是 "inplace" 的,也就是说:
Online 过程一定是 inplace 的,但 inplace 过程不一定是 Online 的。
3、5.6 开始因为支持 Online DDL,所以在向临时表迁移数据时可能会积攒一些写操作造成页分裂,同时在创建新表时每个数据页还会留 1/16 的空间用于更新操作,所以执行DDL后的表结构不一定是最 "紧凑" 的。
MDL 引发的问题
在上线项目中,如果需要对某个大表的字段进行删除,那么必然会阻塞该表的所有增删改查操作,如果该表存储了热点数据,那么就会阻塞大量的操作,最终导致数据库崩溃。
解决
1、查看当前是否存在长事务,如果存在,先尽快将其提交,防止长事务的MDL写锁阻塞
2、为 DDL 的操作设置过期时间,如果时间内没有成功执行就取消。可以使用 Github 的开源工具 gh-ost。