MDL(Meta Data LocK)的作用
在MySQL5.1及之前的版本中,如果有未提交的事务trx,当执行DROP/RENAME/ALTER TABLE RENAME操作时,不会被其他事务阻塞住。这会导致如下问题(MySQL bug#989)
master: 未提交的事务,但SQL已经完成(binlog也准备好了),表schema发生更改,在commit的时候不会被察觉到.
slave: 在binlog里是以事务提交顺序记录的,DDL隐式提交,因此在备库先执行DDL,后执行事务trx,由于trx作用的表已经发生了改变,因此trx会执行失败。 在DDL时的主库DML压力越大,这个问题触发的可能性就越高
在5.5引入了MDL(meta data lock)锁来解决在这个问题
MDL锁的类型
metadata lock也是一种锁。每个metadata lock都会定义锁住的对象,锁的持有时间和锁的类型
属性 | 范围 | 作用 |
---|---|---|
GLOBAL | 全局锁 | 主要作用是防止DDL和写操作的过程中执行 set golbal_read_only =on 或flush tables with read lock; |
commit | 提交保护锁 | 主要作用是执行flush tables with read lock后,防止已经开始在执行的写事务提交 |
SCHEMA | 库锁 | 对象 |
TABLE | 表锁 | 对象 |
FUNCTION | 函数锁 | 对象 |
PROCEDURE | 存储过程锁 | 对象 |
TRIGGER | 触发器锁 | 对象 |
EVENT | 事件锁 | 对象 |
这些锁具有以下层级关系
MDL锁的简单示例
在实际工作中,最常见的MDL冲突就DDL的操作被没用提交的事务所阻塞。 我们下面通过一个具体的实例来演示DDL加MDL锁的过程。在这个实例中,利用gdb来跟踪DDL申请MDL锁的过程。
会话1:
1 | mysql> create table ti(id int primary key, c1 int, key(c1)) engine=InnoDB |
再开启第二个会话,利用gdb来跟踪mysql加MDL的过程 会话2:
1 | [root@localhost mysql]# ps -ef|grep mysql |
开启第三个会话
1 | mysql> alter table ti stats_auto_recalc=1; |
在会话2中执行下面的操作
1 | (gdb) p mdl_request |
从上面的输出中,我只能看到申请了一个语句级别的MDL_INTENTION_EXCLUSIVE。并没有看到什么其他有意义的信息。我们继续gdb跟踪
1 | (gdb) p *(mdl_request->next_in_list) |
从上面的输出中,我们看到了需要在test(见输出中的 m_ptr = “\001test)数据库上加一把事务级的MDL_INTENTION_EXCLUSIVE锁。它并没有告诉我们最终的MDL会落在哪个对象上。我们继续跟踪
1 | $4 = {type = MDL_SHARED_UPGRADABLE, duration = MDL_TRANSACTION, next_in_list = 0x0, prev_in_list = 0x7f697002a568, ticket = 0x0, key = {m_length = 9, m_db_name_length = 4, |
从上面的输出中,我们可以看出最终是要在test数据库的ti对象上加一把MDL_SHARED_UPGRADABLE锁。在做DDL时会先加MDL_SHARED_UPGRADABLE锁,然后升级到MDL_EXCLUSIVE锁
我来执行下面的过程 会话1
1 | mysql> commit; |
会话2
1 | (gdb) p *mdl_request |
从上面的输出中,我们看到了最终是在test.ti上申请了事务级别的MDL_EXCLUSIVE锁。
会话3
1 | mysql> alter table ti stats_auto_recalc=1; |
小结
本例只是简单的演示了,在同一个事务的不同时期加的不同的MDL的锁。MYSQL中DDL的操作不属于事务操作的范围。这就给mysql主备基于语句级别同步带来了困难。mysql主备在同步的过程中,为了保证主备结构一致性,而引入了MDL机制。为了尽可能的降低MDL带来的影响。请在业务低谷的时候,执行DDL操作。
本文地址:http://xnerv.wang/trace-metadata-lock-procedure-using-gdb/
转载自:利用gdb跟踪MDL加锁过程