MySQL源码学习:MDL字典锁

什么是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进行上锁。

  1. Acquire_lock:  
  2.     if (mdl_request contains the needed ticket )  
  3.     return ticket;  
  4.     End if;  
  5.     Create a ticket;  
  6.     If (!find lock in lock_sys)  
  7.     Create a lock;  
  8.     End if  
  9.     If (lock can be granted to mdl_request)  
  10.     Set lock to ticket;  
  11.     Set ticket to mdl_request;  
  12.     Else 
  13.     Wait for lock  
  14. End if 

稍微解释下,首先是在mdl_request本身去查看有没有相等的或者stronger的ticket,如果存在,则直接使用。否则创建一个 ticket,查找上锁对象对应的lock,没有则创建。检查lock是否可以被赋给本事务,如果可以直接返回,否则等待这个lock;

锁等待与唤醒

字典对象的锁等待是发生在两个事物对同一对象上不兼容的锁导致的。当然,由于lock的唯一性,先到先得,后到的只能等待。

如何判断一个lock是否可以grant给一个TX?这需要结合lock结构来看了,lock上有两个成员,grant和wait,grant代表此 lock允许的事物都上了哪些锁,wait表示等待的事务需要上哪些锁。其判断一个事物是否可以grant的逻辑如下:

  1. If(compatible(lock.grant, tx.locktype))  
  2.     If (compatible(lock.wait, tx.locktype))  
  3.     return can_grant;  
  4.     End if  
  5. 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函数中,重置等待时间的标记位进行唤醒,重点代码如下:

  1. if (can_grant_lock(ticket->get_type(), ticket->get_ctx()))  
  2.     {  
  3.       if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))  
  4.       {  
  5.         /*  
  6.           Satisfy the found request by updating lock structures.  
  7.           It is OK to do so even after waking up the waiter since any 
  8.           session which tries to get any information about the state of 
  9.           this lock has to acquire MDL_lock::m_rwlock first and thus,  
  10.           when manages to do so, already sees an updated state of the  
  11.           MDL_lock object.  
  12.         */  
  13.         m_waiting.remove_ticket(ticket);  
  14.         m_granted.add_ticket(ticket);  
  15.     } 

今天把mdl系统总体上看了一下,对锁的请求、等待以及唤醒有了初步了解。并发性的问题是最难调试的,大家如果想做锁方面的实验,可以利用VS调试中的冻结线程的功能,这样就可以确保并发情况控制完全按照你设计思路去呈现。

原文链接:http://www.cnblogs.com/nocode/archive/2011/12/15/2289507.html

责任编辑:林师授
来源: 心中无码的博客 MySQL MDL

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/229626.html<

(0)
运维的头像运维
上一篇2025-04-18 20:48
下一篇 2025-04-18 20:50

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注