开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 2109|回复: 1
打印 上一主题 下一主题
收起左侧

[Mysql] MySQL源码学习:MDL字典锁

[复制链接]

跳转到指定楼层
楼主
发表于 2013-2-4 09:28:59 | 只看该作者 回帖奖励 |正序浏览 |阅读模式   山东省聊城市
什么是MDL
MDL,Meta Data lock,元数据锁,一般称为字典锁。字典锁与数据锁相对应。字典锁是为了保护数据对象被改变,一般是一些DDL会对字典对象改变,如两个TX,TX1先查询表,然后TX2试图DROP,字典锁就会lock住TX2,知道TX1结束(提交或回滚)。数据锁是保护表中的数据,如两个TX同时更新一行时,先得到row lock的TX会先执行,后者只能等待。
MDL的设计目标
字典锁在设计的时候是为了数据库对象的元数据。到达以下3个目的。
1. 提供对并发访问内存中字典对象缓存(table definatin cache,TDC)的保护。这是系统的内部要求。
2. 确保DML的并发性。如TX1对表T1查询,TX2同是对表T1插入。
3. 确保一些操作的互斥性,如DML与大部分DDL(ALTER TABLE除外)的互斥性。如TX1对表T1执行插入,TX2执行DROP TABLE,这两种操作是不允许并发的,故需要将表对象保护起来,这样可以保证binlog逻辑的正确性。(貌似之前的版本存在字典锁是语句级的,导致 binlog不合逻辑的bug。)
支持的锁类型
数据库理论中的基本锁类型是S、X,意向锁IS、IX是为了层次上锁而引入的。比如要修改表中的数据,可能先对表上一个表级IX锁,然后再对修改的数据上一个行级X锁,这样就可以保证其他试图修改表定义的事物因为获取不到表级的X锁而等待。
MySQL中将字典锁的类型根据不同语句的功能,进一步细分,细分的依据是对字典的操作和对数据的操作。细分的好处是能在一定程度上提高并发效率,因为如果只定义X和S两种锁,必然导致兼容性矩阵的局限性。MySQL不遗余力的定义了如下的锁类型。
名称
意义
MDL_INTENTION_EXCLUSIVE
意向排他锁,只用于范围上锁
MDL_SHARED
共享锁,用于访问字典对象,而不访问数据。
MDL_SHARED_HIGH_PRIO
只访问字典对象(如DESC TABLE)
MDL_SHARED_READ
共享读锁,用于读取数据(如select)
MDL_SHARED_WRITE
共享写锁,用于修改数据(如update)
MDL_SHARED_NO_WRITE
共享非写锁,允许读取数据,阻塞其他TX修改数据(如alter table)
MDL_SHARED_NO_READ_WRITE
用于访问字典,读写数据
不允许其他TX读写数据
MDL_EXCLUSIVE
排他锁,可以修改字典和数据
可以看到MySQL在ALTER TABLE的时候还是允许其他事务进行读表操作的。需要注意的是读操作的事物需要在ALTER TABLE获取MDL_SHARED_NO_WRITE锁之后,否则无法并发。这种应用场景应该是对一个较大的表进行ALTER时,其他事物仍然可以读,并发性得到了提高。
锁的兼容性
锁的兼容性就是我们经常看到的那些兼容性矩阵,X和S必然互斥,S和S兼容。MySQL根据锁的类型我们也可以知道其兼容矩阵如下:

IX
S
SH
SR
SW
SNW
SNRW
X
IX
1
1
1
1
1
1
1
1
S
1
1
1
1
1
1
1
0
SH
1
1
1
1
1
1
1
0
SR
1
1
1
1
1
1
0
0
SW
1
1
1
1
1
0
0
0
SNW
1
1
1
1
0
0
0
0
SNRW
1
1
1
0
0
0
0
0
X
1
0
0
0
0
0
0
0
1代表兼容,0代表不兼容。你可能发现X和IX竟然兼容,没错,其实这里的IX已经不是传统意义上的IX,这个IX是用在范围锁上,所以和X锁不互斥。
数据结构
涉及到的和锁相关的数据结构主要是如下几个:
MDL_context:字典锁上下文。包含一个事物所有的字典锁请求。
MDL_request:字典锁请求。包含对某个对象的某种锁的请求。
MDL_ticket:字典锁排队。MDL_request就是为了获取一个ticket。
MDL_lock:锁资源。一个对象全局唯一。可以允许多个可以并发的事物同时获得。
涉及到的源码文件主要是sql/mdl.cc
锁资源
锁资源在系统中是共享的,即全局的,存放在static MDL_map mdl_locks;的hash链表中,对于数据库中的一个对象,其hashkey必然是唯一的,对应一个锁资源。多个事务同时对一张表操作时,申请的 lock也是同一个内存对象。获取mdl_locks中的lock需要通过全局互斥量保护起来 mysql_mutex_lock(&m_mutex); m_mutex是MDL_map的成员。
上锁流程
一个会话连接在实现中对应一个THD实体,一个THD对应一个MDL_CONTEXT,表示需要的mdl锁资源,一个MDL_CONTEXT中包含多个MDL_REQUEST,一个MDL_REQUEST即是对一个对象的某种类型的lock请求。每个mdl_request上有一个ticket对象,ticket中包含lock。
上锁的也就是根据MDL_REQUEST进行上锁。

    Acquire_lock:  
  •     if (mdl_request contains the needed ticket )  
        return ticket;  
  •     End if;  
        Create a ticket;  
  •     If (!find lock in lock_sys)  
        Create a lock;  
  •     End if  
        If (lock can be granted to mdl_request)  
  •     Set lock to ticket;  
        Set ticket to mdl_request;  
  •     Else
        Wait for lock  
  • End if
稍微解释下,首先是在mdl_request本身去查看有没有相等的或者stronger的ticket,如果存在,则直接使用。否则创建一个 ticket,查找上锁对象对应的lock,没有则创建。检查lock是否可以被赋给本事务,如果可以直接返回,否则等待这个lock;
锁等待与唤醒
字典对象的锁等待是发生在两个事物对同一对象上不兼容的锁导致的。当然,由于lock的唯一性,先到先得,后到的只能等待。
如何判断一个lock是否可以grant给一个TX?这需要结合lock结构来看了,lock上有两个成员,grant和wait,grant代表此 lock允许的事物都上了哪些锁,wait表示等待的事务需要上哪些锁。其判断一个事物是否可以grant的逻辑如下:

    If(compatible(lock.grant, tx.locktype))  
  •     If (compatible(lock.wait, tx.locktype))  
        return can_grant;  
  •     End if  
  • End if
即首先判断grant中的锁类型和当前事务是否兼容,然后判断wait中的锁类型和当前事务是否兼容。细心的话,会想到,wait中的锁类型是不需要和当前事务进行兼容性比较的,这是不是说这个比较是多余的了?其实也不是,因为wait的兼容性矩阵和上面的矩阵是不一样的,wait的兼容性矩阵感觉是在 DDL等待的情况下,防止DML继续进来(wait矩阵就不写出来了,大家可以去代码里看下)。
比如:
TX1                    TX2                    TX3
SELECT T1
DROP  T1
SELECT T1
这时候TX2会阻塞,TX3也会阻塞,被TX2阻塞,也就是说被wait的事件阻塞了,这样可能就是为了保证在DDL等待时,禁止再做DML了,因为在DDL面前,DML显得确实不是那么重要了。
如何唤醒被等待的事务呢?比如唤醒TX2,当TX1结束时,会调用release_all_locks_for_name,对被锁住的事务进行唤醒,具体操作封装在reschedule_waiters函数中,重置等待时间的标记位进行唤醒,重点代码如下:

    if (can_grant_lock(ticket->get_type(), ticket->get_ctx()))  
  •     {  
          if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))  
  •       {  
            /*  
  •           Satisfy the found request by updating lock structures.  
              It is OK to do so even after waking up the waiter since any
  •           session which tries to get any information about the state of
              this lock has to acquire MDL_lock::m_rwlock first and thus,  
  •           when manages to do so, already sees an updated state of the  
              MDL_lock object.  
  •         */  
            m_waiting.remove_ticket(ticket);  
  •         m_granted.add_ticket(ticket);  
  •     }
今天把mdl系统总体上看了一下,对锁的请求、等待以及唤醒有了初步了解。并发性的问题是最难调试的,大家如果想做锁方面的实验,可以利用VS调试中的冻结线程的功能,这样就可以确保并发情况控制完全按照你设计思路去呈现。

结帖率:37% (7/19)
沙发
发表于 2013-2-12 10:30:10 | 只看该作者   北京市北京市
爆破用啊?   
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:[email protected]
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表