作者简介:姜宇祥,2012 年加入携程,10 年数据库核心代码开发经验,相关开发涉及达梦,MySQL 数据库。现致力于携程 MySQL 的底层研发,为特殊问题定位和处理提供技术支持。

锁是计算机程序运行时协调并发访问同一数据资源的机制。对于数据库系统来说,数据是一种供许多用户共享的资源,那么如何保证数据并发访问的一致性、有效性是必须解决的一个问题。所以,锁对于数据库来说,是非常重要的一个功能。通过各种锁,实现了数据库事务中的隔离性。本篇文章将从源码层面介绍 MySQL 的元数据锁和 InnoDB 的实现。

一、MySQL 的架构与锁

MySQL 在架构上分为两层,服务和存储引擎层。服务层集中了网络通讯、语法分析和计划生成等通用功能;存储引擎层主要负责数据的存储。元数据的并发管理集中在服务层,数据的并发管理在存储引擎层。因此对于元数据的锁在服务层进行实现,数据的隔离特性在存储引擎层实现。本篇将介绍服务层的元数据锁的实现,以及现下使用率最高的具有 ACID 特性的 InooDB 的数据锁。

二、元数据锁

2.1 元数据锁类型

fastapi mysql 项目源码 mysql源码分析_mysql5.6源码分析锁

2.2 元数据锁申请与释放

在申请元数据锁的同时,会指定锁释放的时间。在程序执行到指定位置时,如语句执行结束或者事务执行结束,会检查元数据锁的上锁情况,并释放那些需要在该位置释放的元数据锁。

fastapi mysql 项目源码 mysql源码分析_元数据_02

抽象元数据锁的上锁和释放的过程,整理为如下流程图

fastapi mysql 项目源码 mysql源码分析_MySQL_03

2.3 元数据锁关系

MySQL 的元数据也是有从属关系的。有些元数据进行上锁的同时,需要配合其他元数据锁,这里称这种关系为从属关系。这种从属关系如下图所示,其箭头所指方向为元数据锁所依赖关系。比如在为 SCHEMA 元数据加锁时,需要 GLOBAL 元数据锁。

fastapi mysql 项目源码 mysql源码分析_事务隔离_04

2.4 元数据锁级别

由于对元数据的访问存在不同的需求,因此设置不同级别锁级别,用于对元数据及数据的访问控制。

fastapi mysql 项目源码 mysql源码分析_元数据_05

2.5 元数据锁源码

该部分介绍 MySQL 源码的主要源文件和主要函数。其中源文件以 mdl.h/mdl.cc 为核心,定义了元数据锁的主要数据结构和函数,lock.cc/sql_db.cc 等源文件使用元数据锁所定义的数据结构和函数。

主要源文件及其关系如下,蓝框内所列源文件依赖于红框内的源文件所定义的内容。

fastapi mysql 项目源码 mysql源码分析_元数据_06

主要函数如下图关系所列

fastapi mysql 项目源码 mysql源码分析_mysql5.6源码分析锁_07

三、InnoDB 锁

3.1 事务隔离级简介

存储引擎 InnoDB 实现事务的四个隔离级,也就是读未提交和读提交等四个事务隔离级。所谓事务隔离级,是并发访问中控制数据读写的方式。在这里先简单介绍这四个事务隔离级的来龙去脉,以便于理解 MySQL 的锁机制。

InnoDB 事务采用不同的锁机制,会产生不同的现象。

fastapi mysql 项目源码 mysql源码分析_MySQL_08

事务隔离级和各种现象的关系,“X”表示在该事务隔离级下现象可发生,“--”表示在该事务隔离级下现象不会发生。

fastapi mysql 项目源码 mysql源码分析_mysql5.6源码分析锁_09

3.2 InnoDB 的锁类型

InnoDB 为保护并发访问下的数据,根据不同的粒度对数据进行。

fastapi mysql 项目源码 mysql源码分析_fastapi mysql 项目源码_10

3.3 InnoDB 的锁级别

InnoDB 的锁

fastapi mysql 项目源码 mysql源码分析_MySQL_11

各个级别锁之间存在兼容性问题,如下表格列出各个级别锁之间的兼容性。“X”表示不兼容,“O”表示兼容。

fastapi mysql 项目源码 mysql源码分析_事务隔离_12

3.4 间隙锁

InnoDB 间隙锁(next-key lock)的用处是在 repeatable read 的隔离级下防止幻读现象的出现,所以一定要记住,在其他隔离级下是不会出现间隙锁的。间隙锁的原理是在通过对行锁进行特殊标识(此时的行锁就被称为间隙锁),指出在该范围内行记录的左开右闭区间被封锁,不可进行更新操作。正是采用这种针对行记录之间间隙上锁方式,所以称为间隙锁或者 next-key 锁。

如下图所示,假设索引中的 key 值为 5、17、23 和 29。当执行如下 SQL:begin; select * from t4 where id2=16 forupdate;会对 key:17 创建一个行锁,并标识该行锁为间隙锁,其锁定区间为红框内 6 到 17,也就是这个区间的值域发生了变化,如果再发生变化可能会影响该区域的数据行集合,所以需要锁定该区域为不可更新。

fastapi mysql 项目源码 mysql源码分析_MySQL_13

3.5 源码结构

fastapi mysql 项目源码 mysql源码分析_元数据_14

核心代码包含了有关锁的宏定义和函数定义等锁的类型定义和操作定义,应用代码为使用这些宏和函数的模块。