Mysql中的MDL
首先简单了解一下 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 优化思路就是在获取到锁之后先允许一段时间的读写操作,直到临时表的数据转移完成,再停止其他读写操作,而具体实现就主要分为下面几步,
-
拿MDL写锁
-
降级成MDL读锁,接受读写操作,读操作直接正常返回,写操作会被记录下来,等待后面更新到临时表中。
-
真正做DDL,在 DDL 表记录向临时表转移完成并执行完记录的写操作后开始阻塞读写操作。
-
升级成MDL写锁,进行临时表的替换
-
释放MDL锁
注意点
- Online DDL 整个过程是在 InnoDB 内部执行的,对于 Server 来说并没有什么操作,所以这个操作是 "inplace" 的,也就是说:
Online 过程一定是 inplace 的,但 inplace 过程不一定是 Online 的。
- 5.6 开始因为支持 Online DDL,所以在向临时表迁移数据时可能会积攒一些写操作造成页分裂,同时在创建新表时每个数据页还会留 1/16 的空间用于更新操作,所以执行DDL后的表结构不一定是最 "紧凑" 的。
- 5 .6以后 ALGORITHM=INPLACE 这个参数不会表复制
- 例子:ALTER TABLE tbl_name ADD COLUMN column_name column_definition, ALGORITHM=INPLACE, LOCK=NONE;
MDL 引发的问题
在上线项目中,如果需要对某个大表的字段进行删除,那么必然会阻塞该表的所有增删改查操作,如果该表存储了热点数据,那么就会阻塞大量的操作,最终导致数据库崩溃。
解决
1、查看当前是否存在长事务,如果存在,先尽快将其提交,防止长事务的MDL写锁阻塞
2、为 DDL 的操作设置过期时间,如果时间内没有成功执行就取消。可以使用 Github 的开源工具 gh-ost。
一条小咸鱼