内容简介: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锁》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
UNIX环境高级编程
W.Richard Stevens Stephen A.Rago、Stephen A. Rago / 人民邮电出版社 / 2006-2 / 99.00元
本书是被誉为UNIX编程“圣经”的Advanced Programming in the UNIX Environment一书的更新版。在本书第一版出版后的十几年中,UNIX行业已经有了巨大的变化,特别是影响UNIX编程接口的有关标准变化很大。本书在保持了前一版的风格的基础上,根据最新的标准对内容进行了修订和增补,反映了最新的技术发展。书中除了介绍UNIX文件和目录、标准I/O库、系统数据文件和信......一起来看看 《UNIX环境高级编程》 这本书的介绍吧!