

浅析MYSQL MDL锁
source link: https://www.tuicool.com/articles/bYjUnyI
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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事务锁>>
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK