MySQL:理解MDL Lock


本文基于源码版本5.7.14

水平有限,有误请谅解

笔者已经将加好MDL 获取过程和释放过程的版本放到了github如下:

https://github.com/gaopengcarl/percona-server-locks-detail-5.7.22

供参考
作者深入理解主从原理专栏
https://www.jianshu.com/nb/43148932MySQL中MDL锁一直是一个比较让人比较头疼的问题,我们谈起堵塞一般更加倾向于InnoDB层的row lock(gap lock/next key lock/key lock),因为它很好理解也很好观察。而对于MDL Lock考虑就少一些,因为它实在不好观察,只有出现问题查看show processlist的时候,可以看到简单的所谓的‘Waiting for table metadata lock’之类的状态,其实MDL Lock是MySQL上层一个非常复杂的子系统,有自己的死锁检测机制。

大家一般说是不是锁表了很大一部分就和MDL Lock有关,可见的它的关键性和严重性,笔者也是根据自己的需求学习了一些,且没有能力阅读全部的代码,但是笔者通过增加一个TICKET的打印函数让语句的MDL Lock加锁流程全部打印出来方便学习,下面从一些基础概念说起然后告诉大家笔者是如何做的打印功能,最后对每种MDL TYPE可能出现的语句进行测试和分析。如果大家对基本概念和增加打印函数不感兴趣可直接参考第五部分语句加MDL Lock测试和分析,希望这些测试能够帮助到大家诊断问题。
刚好最近笔者遇到一次MDL Lock出现死锁的情况会在下篇文章中给出案例,本文只看理论。我们主要研究的类型如下:MDL_INTENTION_EXCLUSIVE(IX)

MDL_SHARED(S)

MDL_SHARED_HIGH_PRIO(SH)

MDL_SHARED_READ(SR)

MDL_SHARED_WRITE(SW)

MDL_SHARED_WRITE_LOW_PRIO(SWL)

MDL_SHARED_UPGRADABLE(SU)

MDL_SHARED_READ_ONLY(SRO)

MDL_SHARED_NO_WRITE(SNW)

MDL_SHARED_NO_READ_WRITE(SNRW)

MDL_EXCLUSIVE(X)
第五部分会对每种类型进行详细的测试和解释。在MDL中MDL_KEY按照namespace+DB+OBJECT_NAME的方式进行表示,所谓的namespace也比较重要下面是namespace的分类:本文我们主要对GLOBAL/SCHEMA/TABLE namespace进行描述,而对于COMMIT namespace是提交的时候会用到的如果遇到等待,状态为‘Waiting for commit lock’,一般为FTWRL堵塞COMMIT。可参考我的《深入理解MySQL主从原理》15节。其他namespace不做描述。下面是源码注释:

这里兼容矩阵是学习锁堵塞的重点,类型很多比Innodb row lock类型要多很多,不用记住,只需要遇到能知道。这个对应源码的enum_mdl_duration,通常我们需要关注MDL Lock是事务提交后释放还是语句结束后释放,实际上就是这个,这对MDL lock堵塞的范围很重要。我直接复制源码的解释。使用两种不同的方式目的在于优化MDL Lock的实现,下面是源码的注释,可做适当了解:也就是通过语句解析后需要获得的MDL Lock的需求,然后通过这个类对象在MDL子系统中进行MDL Lock申请,大概包含如下一些属性:就是实际的namespace+DB+OBJECT_NAME,整个放到一个char数组里面,他会在MDL_LOCK和MDL_REQUEST中出现。如同门票一样,如果获取了MDL Lock必然给MDL_request返回一张门票,如果等待则不会分配。源码MDL_context::acquire_lock可以观察到。部分属性如下:每一个MDL_key都会对应一个MDL_lock,其中包含了所谓的GRANTED链表和WAIT链表,考虑它的复杂性,可以直接参考源码注释也非常详细,这里给出我所描述的几个属性如下:这是整个MySQL线程和MDL Lock子系统进行交互的一个所谓的上下文结构,其中包含了很多方法和属性,我比较关注的属性如下:源码给出了所有的等待标记如下:我们常见的是:学习MDL Lock最好的方式当然是获取一条语句锁加的所有MDL Lock,包含加锁、升级、降级和释放的流程。虽然5.7加入诊断MDL Lock的方法:但是对于每个语句获取的所有MDL Lock的流程仍然不好观察,因此我加入了打印函数:并且在mdl_ticket类中增加了这个函数原型为友元函数:主要捕获MDL Lock的加锁信息打印到err日志中,包含的信息如下:上面这些信息都在前面进行过描述了。具体的输出信息如下:这实际上和metadata_locks中的信息差不多,如下:一旦有了这个函数我们只需要在加锁、升级、降级和释放的位置进行适当添加就可以了。既然我们要研究MDL Lock的加锁?升级?降级,那么我们就必要找到他们的函数入口,然后在合适的位置增加打印函数my_print_ticket进行观察,下面标示出打印位置。这个锁会在很多操作的时候都会出现,比如做任何一个DML/DDL操作都会触发,实际上DELTE/UPDATE/INSERT/FOR UPDATE等DML操作会在GLOBAL 上加IX锁,然后才会在本对象上加锁。而DDL 语句至少会在GLOBAL 上加IX锁,对象所属 SCHEMA上加IX锁,本对象加锁。下面是 DELETE 触发的 GLOABL IX MDL LOCK:我们注意一样它的持续周期为语句级别。下面是 ALETER 语句触发的GLOABL IX MDL Lock:所以这个MDL Lock无所不在,而只有是否兼容问题,如果不兼容则堵塞。scope lock的IX类型一般都是兼容的除非遇到S类型,下面讨论。这把锁一般用在flush tables with read lock中,如下:我们注意到其namspace为GLOBAL和COMMIT显然他们是scope lock ,他们的TYPE为S,那么很显然根据兼容性原则scope lock的MDL IX和MDL S 不兼容, flush tables with read lock 就会堵塞所有DELTE/UPDATE/INSERT/FOR UPDATE等DML和DDL操作,并且也会堵塞commit操作。这个锁基本上大家也是经常用到只是没感觉到而已,比如我们一般desc操作,兼容矩阵如下:

操作记录如下:这中类型的优先级比较高,但是其和X不兼容。注意持续时间为MDL_TRANSACTION 。这把锁一般用在非当前读取的select中,兼容性如下:

操作记录如下:这里还是要提及一下平时我们偶尔会出现select开发云主机域名也堵住的情况(比如DDL的某个阶段需要对象MDL X锁)。我们不得不抱怨MySQL居然会堵塞select其实这里也就是object mdl lock X 和SR 不兼容的问题(参考前面的兼容矩阵)。注意持续时间为MDL_TRANSACTION 。这把锁一般用于DELTE/UPDATE/INSERT/FOR UPDATE等操作对table的加锁(当前读),不包含DDL操作,但是要注意DML操作实际上还会有一个GLOBAL的IX的锁,前面已经提及过了,这把锁只是对象上的,兼容性如下:
操作记录如下:注意持续时间为MDL_TRANSACTION 。这把锁很少用到源码注释只有如下:不做解释了。这把锁一般在ALTER TABLE语句中会用到,他可以升级为SNW,、SNRW、X,同时至少X锁也可以降级为SU实际上在Innodb ONLINE DDL中非常依赖它,由于它的存在那么DML(SW)和SELECT(SR)都不会堵塞,兼容性如下:

我们有必要研究一下他的兼容性,可以看到 OBJECT LOCK中(SELECT)SR和(DML)SW都是允许的,而在SCOPED LOCK中虽然DML DDL都会在GLOBAL 上锁,但是其类型都是IX。所以这个SU锁不堵塞DML/SELECT 读写操作进入Innodb引擎层,它是ONLINE DDL的基础。如果不兼容你都进入不了Innodb引擎层,更谈不上什么ONLINE DDL,注意我这里说的ALGORITHM=INPLACE的ONLINE DDL。操作日志记录:我们需要简单分析一下,获得testsort12表上的MDL Lock大概流程如下:不管如何这个ALTER操作还是比较费时的,从时间我们看到2017-08-03T19:46:54降级完成(SU)到2017-08-03T19:47:00这段时间,实际上是最耗时的实际上这里就是实际的Inplace重建,但是这个过程实际在MDL SU模式下所以不会堵塞DML/SELECT操作。这里再给大家提个醒,所谓的ONLINE DDL只是在Inplace重建阶段不堵塞DML/SELECT操作,还是尽量在数据库压力小的时候操作,如果有DML没有提交或者SELECT没有做完这个时候SW或者SR必然堵塞X,而X为高优先级能够堵塞所有操作。这样导致的现象就是由于DML未提交会堵塞DDL操作,而DDL操作会堵塞所有操作,基本对于这个TABLE的表全部操作堵塞(SW堵塞X,X堵塞所有操作)。

而对于ALGORITHM=COPY 在COPY阶段用的是SNW锁,接下来我就先来看看SNW锁。
SU可以升级为SNW而SNW可以升级为X,如前面所提及的用于ALGORITHM=COPY 中,保护数据的一致性。先看看它的兼容性如下:

从兼容矩阵可以看到,本锁不会堵塞SR,但是堵塞SW,当然也就堵塞了DML(SW)而SELECT(SR)不会堵塞,下面是部分操作记录日志:我们可以发现如下:2017-08-03T20:07:58.413308到2017-08-03T20:08:25.392006就是实际COPY的时间,可见整个COPY期间只能SELECT,而不能DML。也是ALGORITHM=COPY和ALGORITHM=INPLACE的一个关键区别。用于LOCK TABLES READ 语句,兼容性如下:

根据兼容性可以发现,堵塞DML(SW)但是SELECT(SR)还是可以的。下面是操作日志:用于LOCK TABLES WRITE语句,兼容性如下:

可以看到DML(SW)和SELECT(SR)都被它堵塞,但是还可以DESC(SH)。

操作日志记录如下:
除此之外可以发现语句还需要GLOBAL和SCHEMA上的IX锁,换句话说flush tables with read lock; 会堵塞‘lock table testsort12 write’,但是‘lock table testsort12 read’却不会堵塞。用于各种DDL操作,实际上基本全部的DDL都会涉及到这个锁,即便是ONLINE DDL也会在准备和提交阶段获取本锁,因此ONLINE DDL不是完全不堵塞的,只是堵塞时间很短很短,兼容性如下:
我们在验证SU和SNW MDL Lock类型的时候已经看到了操作记录,不做补充了。作者微信:gp_22389860

相关推荐: mysql迁移至8.0时应该注意什么

这篇文章主要为大家展示了mysql迁移至8.0时应该注意什么,内容简而易懂,希望大家可以学习一下,学习完之后肯定会有收获的,下面让小编带大家一起来看看吧。密码模式PDO::__construct(): The server requested authenti…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 06/05 12:02
下一篇 06/05 12:02

相关推荐