浅析MYSQL MDL锁

栏目: 数据库 · 发布时间: 5年前

内容简介: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 的许可或请求, 会同时挂在两处:

  1. 挂在当前线程的 MDL_Context 中, 每个线程有3个 ticket 链表,分别对应当前持有的 statement 锁、 transaction 锁和 explicit 锁,放在   MDL_context::m_tickets

  2. 挂在 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

浅析MYSQL MDL锁

二. 常见SQL加锁流程

1. select语句操作MDL锁流程:

  1. open table 阶段加事务级,表级, MDL_SHARED_READ

  2. commit 阶段,释放锁

浅析MYSQL MDL锁

2. alter语句操作MDL锁流程:

  1. 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

    浅析MYSQL MDL锁

    3. DML语句操作MDL锁流程:

    1. Opening tables 阶段,加共享锁

    • 加语句级,全局, MDL_INTENTION_EXCLUSIVE

    • 加事务级,表级, MDL_SHARED_WRITE

  • 事务提交阶段,释放 MDL

    • 语句执行完,释放语句级,全局, MDL_INTENTION_EXCLUSIVE

    • commit 后,释放事务级,表级, MDL_SHARED_WRITE

    浅析MYSQL MDL锁

    4. flush table with read lock(ftwrl) 语句操作MDL锁流程:

    1. MDL_EXPLICIT GLOBAL (lock_global_read_lock)

    2. 清理表缓存 (close_cached_tables)

    3. MDL_EXPLICIT COMMIT (make_global_read_lock_block_commit)

    4. unlock 后释放 GLOBAL 锁和   COMMIT

    浅析MYSQL MDL锁

    问题:

    从上述SQL加锁流程中,解释为什么在事务中执行DML语句后,可以执行flush  table with read lock?

    相关解释可以参考 下一篇文章:<< flush table with write lock 实现MDL事务锁>>


    以上所述就是小编给大家介绍的《浅析MYSQL MDL锁》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

    查看所有标签

    猜你喜欢:

    本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

    UNIX环境高级编程

    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环境高级编程》 这本书的介绍吧!

    JSON 在线解析
    JSON 在线解析

    在线 JSON 格式化工具

    XML、JSON 在线转换
    XML、JSON 在线转换

    在线XML、JSON转换工具

    RGB HSV 转换
    RGB HSV 转换

    RGB HSV 互转工具