内容简介:MySQL为了保护数据字典元数据,使用了metadata lock,即MDL锁,保证在并发的情况下,结构变更的一致性。本文基于spider3.x(MySQL5.7)从代码实现角度分析了常用SQL语句的MDL加锁实现。字典锁在设计的时候是为了数据库对象的元数据。到达以下3个目的:
MySQL为了保护数据字典元数据,使用了metadata lock,即MDL锁,保证在并发的情况下,结构变更的一致性。本文基于spider3.x(MySQL5.7)从代码实现角度分析了常用 SQL 语句的MDL加锁实现。
一. 基本概念
1. MDL的设计目标
字典锁在设计的时候是为了数据库对象的元数据。到达以下3个目的:
-
提供对并发访问内存中字典对象缓存的保护。
-
确保DML的并发性。如事务1对表T1查询,事务2同是对表T1插入。
-
确保一些操作的互斥性,如
DML与大部分DDL(ALTER TABLE除外)的互斥性。如事务1对表table1执行插入,事务2执行DROP TABLE,这两种操作是不允许并发的,故需要将表对象保护起来。
2. MDL锁持有时间
-
MDL_STATEMENT: 语句范围的,语句结束自动释放 -
MDL_TRANSACTION: 事务范围的,事务结束时自动释放 -
MDL_EXPLICIT: 显式锁,由flush table with read lock这种获取,需要通过unlock tables释放
3. MDL锁粒度
MDL_lock 分为 MDL_scoped_lock 和 MDL_object_lock 其中 scope_lock 包括:
-
GLOBAL: 用于global read lock,例如FLUSH TABLES WITH READ LOCK。 -
COMMIT: 主要用于global read lock后,阻塞事务提交。
object_lock 包括:
-
TABLESPACE/SCHEMA: 用于保护tablespace/schema。 -
FUNCTION/PROCEDURE/TRIGGER/EVENT: 用于保护function/procedure/trigger/event。 -
USER_LOCK: 用于对特定字符串上锁。
4. MDL锁类型
-
MDL_INTENTION_EXCLUSIVE(IX)意向排他锁,锁定一个范围,用在GLOBAL/SCHEMA/COMMIT粒度。 -
MDL_EXCLUSIVE(X)排他锁,持有该锁连接可以修改表结构和表数据,使用在CREATE/DROP/RENAME/ALTER TABLE语句。 -
MDL_SHARED(S)用在只访问元数据信息,不访问数据。例如flush table with read lock第一阶段 -
MDL_SHARED_HIGH_PRIO(SH)用于访问information_schema的表。例如:select * from information_schema.tables; -
MDL_SHARED_READ(SR)访问表结构并且读表数据,例如:SELECT * FROM t; -
MDL_SHARED_WRITE(SW)访问表结构且写表数据, 例如:LOCK TABLE t WRITE -
MDL_SHARED_UPGRADABLE(SU)可升级锁,用在alter table的第一阶段,使alter table的时候不阻塞DML,防止其他DDL,减少锁等待时间。 -
MDL_SHARED_NO_WRITE(SNW)持有该锁可以读取表metadata和表数据,同时阻塞所有的表数据修改操作,允许读。可以升级到X锁。用在ALTER TABLE第一阶段,拷贝原始表数据到新表,允许读但不允许更新。 -
MDL_SHARED_NO_READ_WRITE(SNRW) -
MDL_SHARED_READ_ONLY(SRO) -
MDL_SHARED_WRITE_LOW_PRIO(SWLP)
5. 兼容性
Scope 锁活跃锁和请求锁兼容性矩阵, grant_matrix :
| Type of active |
Request | scoped lock |
type | IS(*) IX S X |
---------+------------------+
IS | + + + + |
IX | + + - - |
S | + - + - |
X | + - - - |
Scope 锁等待锁和请求锁优先级矩阵, wait_matrix :
| Pending |
Request | scoped lock |
type | IS(*) IX S X |
---------+-----------------+
IS | + + + + |
IX | + + - - |
S | + + + - |
X | + + + + |
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
object 上已持有锁和请求锁的兼容性矩阵, grant_matrix :
Request | Granted requests for lock |
type | S SH SR SW SU SRO SNW SNRW X |
----------+---------------------------------------+
S | + + + + + + + + - |
SH | + + + + + + + + - |
SR | + + + + + + + - - |
SW | + + + + + - - - - |
SU | + + + + - + - - - |
SRO | + + + - + + + - - |
SNW | + + + - - + - - - |
SNRW | + + - - - - - - - |
X | - - - - - - - - - |
SU -> X | - - - - 0 - 0 0 0 |
SNW -> X | - - - 0 0 - 0 0 0 |
SNRW -> X | - - 0 0 0 0 0 0 0 |
object 上等待锁和请求锁的优先级矩阵, wait_matrix :
Request | Pending requests for lock |
type | S SH SR SW SU SRO SNW SNRW X |
----------+--------------------------------------+
S | + + + + + + + + - |
SH | + + + + + + + + + |
SR | + + + + + + + - - |
SW | + + + + + + - - - |
SU | + + + + + + + + - |
SRO | + + + - + + + - - |
SNW | + + + + + + + + - |
SNRW | + + + + + + + + - |
X | + + + + + + + + + |
SU -> X | + + + + + + + + + |
SNW -> X | + + + + + + + + + |
SNRW -> X | + + + + + + + + + |
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
tips:
1.如何判断一个事务是否能够得到锁呢? 获取锁时需要先判断是否和已经授予的锁模式 (MDL_lock.m_granted) 冲突,如果不冲突再判断和等待队列 (MDL_lock.m_waiting) 中的请求是否冲突,都不冲突才能获取锁,否则进入 m_waiting 队列等待。源码中的注释:
New lock request can be satisfied if: - There are no incompatible types of satisfied requests in other contexts - There are no waiting requests which have higher priority than this request when priority was not ignored.
2. wait_matrix 有什么作用? wait_matrix 中,为什么所有锁都与自身兼容 ? 相关解释见下一篇文章:<< flush table with write lock 实现MDL事务锁>>
6. 数据结构
MDL_lock :锁资源。一个对象全局唯一。可以允许多个可以并发的事物同时获得,存储在全局结构 mdl_locks (MDL_map) 中。
MDL_context :字典锁上下文。包含一个事物所有的字典锁请求。为 MDL 上下文接口,表示一个资源竞争者, THD 实现了这个接口, 即一个 Mysqld 的线程可以是 MDL_lock 的资源竞争者,每一个 MDL_context 只能等待在一个锁上,用 MDL_context::m_waiting_for 表示。
MDL_request :字典锁请求。包含对某个对象的某种锁的请求。
MDL_ticket :字典锁排队。 MDL_request 就是为了获取一个 ticket 。表示 MDL_lock 的许可或请求, 会同时挂在两处:
-
挂在当前线程的
MDL_Context中, 每个线程有3个ticket链表,分别对应当前持有的statement锁、transaction锁和explicit锁,放在MDL_context::m_tickets中 -
挂在
MDL_lock的队列中, 通过MDL_ticket.next_in_lock/prev_in_lock组织链表.MDL_lock的队列分为两种, 一个MDL_ticket可能会挂在其中之一
-
挂在
MDL_lock的等待队列 (MDL_lock.m_waiting) 中, 表示MDL_ticket的owner (MDL_context)正在等待该资源(MDL_lock) -
挂在
MDL_lock的已许可队列(MDL_lock.m_granted)中, 表示MDL_ticket的owner (MDL_context)已经获得该资源(MDL_lock)
一个 session 连接在实现中对应一个 THD 实体,一个 THD 对应一个 MDL_CONTEXT ,表示需要的 MDL 锁资源,一个 MDL_CONTEXT 中包含多个 MDL_REQUEST ,一个 MDL_REQUEST 即是对一个对象的某种类型的 lock 请求。每个 mdl_request 上有一个 ticket 对象, ticket 中包含 lock 。 MDL_ticket 和 MDL_lock 的关系是多对一, 可以同时有多个资源许可在竞争一个资源, 或者多个资源许可可在共享一个资源。 MDL_context 和 MDL_ticket 的关系是一对多, 一个竞争者可以同时申请/获得多个资源的许可;
数据结构间关系见下图,涉及到的源码文件主要是 sql/mdl.cc
二. 常见SQL加锁流程
1. select语句操作MDL锁流程:
-
open table阶段加事务级,表级,MDL_SHARED_READ -
commit阶段,释放锁
2. alter语句操作MDL锁流程:
-
Opening tables阶段,加共享锁
-
加语句级,全局,
MDL_INTENSION_EXCLUSIVE锁 -
加事务级,
schema级,MDL_INTENSION_EXCLUSIVE锁 -
加
MDL_SHARED_UPGRADABLE锁,升级到MDL_SHARED_NO_WRITE锁
操作数据, copy data ,流程如下:
-
创建临时表
tmp,重定义tmp为修改后的表结构 -
从原表读取数据插入到
tmp表
将 MDL_SHARED_NO_WRITE 读锁升级到 MDL_EXCLUSIVE 锁
-
删除原表,将
tmp重命名为原表名 -
alter结束,释放语句级,全局,MDL_INTENSION_EXCLUSIVE锁
事务提交阶段,释放 MDL 锁
-
释放事务级
MDL_INTENTION_EXCLUSIVE锁 -
释放事务级
MDL_EXCLUSIVE锁
3. DML语句操作MDL锁流程:
-
Opening tables阶段,加共享锁
-
加语句级,全局,
MDL_INTENTION_EXCLUSIVE锁 -
加事务级,表级,
MDL_SHARED_WRITE锁
事务提交阶段,释放 MDL 锁
-
语句执行完,释放语句级,全局,
MDL_INTENTION_EXCLUSIVE锁 -
commit后,释放事务级,表级,MDL_SHARED_WRITE锁
4. flush table with read lock(ftwrl) 语句操作MDL锁流程:
-
加
MDL_EXPLICIT GLOBAL锁(lock_global_read_lock) -
清理表缓存
(close_cached_tables) -
加
MDL_EXPLICIT COMMIT锁(make_global_read_lock_block_commit) -
unlock后释放GLOBAL锁和COMMIT锁
问题:
从上述SQL加锁流程中,解释为什么在事务中执行DML语句后,可以执行flush table with read lock?
相关解释可以参考 下一篇文章:<< flush table with write lock 实现MDL事务锁>>
以上所述就是小编给大家介绍的《浅析MYSQL MDL锁》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解计算机系统(原书第3版)
Randal E.Bryant、David O'Hallaron / 龚奕利、贺莲 / 机械工业出版社 / 2016-11 / 139.00元
和第2版相比,本版内容上*大的变化是,从以IA32和x86-64为基础转变为完全以x86-64为基础。主要更新如下: 基于x86-64,大量地重写代码,首次介绍对处理浮点数据的程序的机器级支持。 处理器体系结构修改为支持64位字和操作的设计。 引入更多的功能单元和更复杂的控制逻辑,使基于程序数据流表示的程序性能模型预测更加可靠。 扩充关于用GOT和PLT创建与位置无关代码的......一起来看看 《深入理解计算机系统(原书第3版)》 这本书的介绍吧!