和MySQL打交道比较多的朋友,肯定遇到过 "Waiting for table metadata lock"或者由于MDL导致的故障,不过本文介绍MDL锁之前,我们先看一个著名的 bug#989 大致情况如下:
session1:
BEGIN; INSERT INTO t ... ; COMMIT;
session2:
DROP TABLE t;
然后上面的操作流程在binlog记录的顺序是
DROP TABLE t; BEGIN; INSERT INTO t ... ; COMMIT;
很显然备库执行binlog时会先删除表t,然后执行insert 会报1032 error,导致复制中断。为了解决该bug,MySQL 在5.5.3引入了MDL锁。
顾名思义,metadata lock即 元数据锁 。在数据库中元数据即数据字典信息包括db,table,function,procedure,trigger,event等。metadata lock主要为了保证元数据的一致性,用于处理不同线程操作同一数据对象的同步与互斥问题,如:
session1:
begin ; select * from t1 for update ...
session2:
begin ; alter table t1 add column str varchar(30);
在以上两个事务中,s1 s2都会操作表t1,但由于s1首先获取t1的锁,而s2在获得锁的时候由于互斥,所以只能等待;
其实5.5也有类似保护元数据的机制,只是没有明确提出MDL概念而已。但是5.5之前版本(比如5.1)与5.5之后版本在保护元数据这块有一个显著的不同点是, 5.1对于元数据的保护是语句级别的,5.5对于metadata的保护是事务级别的 。所谓语句级别,即语句执行完成后,无论事务是否提交或回滚,其表结构可以被其他会话更新;而事务级别则是在事务结束后才释放MDL。引入MDL后,主要解决2个问题:
本文浅显的介绍MDL的来龙去脉,想比较详细的了解MDL的前生今世的读者请移步@印风的文章 《MySQL metadata lock的前世今生(5.1=>5.7)》
MySQL引入MDL之后 也会带来一定的负面影响 比如 “wait for global read lock” 或者 “wait for table lock” 之类的锁等待,当遇到大查询,同时遇到一个DDL,基本就能造成一个故障了。后面的文章会深入介绍MDL的实现原理和运行机制,加入一些常见的案例和解决方法。