MySQL · 引擎特性 · InnoDB index lock前世今生

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 前言 InnoDB并发过程中使用两类锁进行同步。 1. 事务锁 维护在不同的Isolation level下数据库的Atomicity和Consistency两大基本特性。 InnoDB定义了如下的lock mode: /* Basic lock modes */ enum lock_m

前言

InnoDB并发过程中使用两类锁进行同步。

1. 事务锁
维护在不同的Isolation level下数据库的Atomicity和Consistency两大基本特性。

InnoDB定义了如下的lock mode:

/* Basic lock modes */
enum lock_mode {
LOCK_IS = 0,     /* intention shared */
LOCK_IX,     /* intention exclusive */
LOCK_S,          /* shared */
LOCK_X,          /* exclusive */
LOCK_AUTO_INC,     /* locks the auto-inc counter of a table
in an exclusive mode */
LOCK_NONE,     /* this is used elsewhere to note consistent read */
LOCK_NUM = LOCK_NONE, /* number of lock modes */
LOCK_NONE_UNSET = 255
};

2. 内存锁
为了维护内存结构的一致性,比如Dictionary cache、sync array、trx system等结构。
InnoDB并没有直接使用glibc提供的库,而是自己封装了两类:
1. 一类是mutex,实现内存结构的串行化访问
2. 一类是rw lock,实现读写阻塞,读读并发的访问的读写锁

读者如果有兴趣,可以直接翻阅InnoDB的代码,这里我们主要介绍index lock所使用的rw lock。

InnoDB index lock

InnoDB默认使用B-Tree结构来保存数据,如下图所示的B-Tree结构:
InnoDB B-Tree结构

这个B-Tree一共有两类节点,一类是node(branch) block,一类是leaf block,对于内存中的每一个block,都有一个rw lock与之相对应,用于保护block内部结构的一致性,阻塞并发修改。每一个index在内存中保持着一个index字典对象,即dict_index_t,并对应着一个index lock,同样属于rw lock类型,用于保护B-Tree的平衡树结构。

所以,InnoDB为每一个index,维护两种rw lock:
1. index级别的,用于保护B-Tree结构不被破坏
2. block级别的,用于保护block内部结构不被破坏

很明显,rw lock 锁保护的对象的级别越高,冲突的可能性就越大,并发的瓶颈也就越容易出现。

InnoDB index lock的处理场景分析

  1. 我们先来看rw lock的模型,rw lock一共使用两类lock mode,即S锁和X锁,其相容性矩阵是:
  | S| X|
--+--+--+
S | o| x|
--+--+--+
X | x| x|
--+--+--+

按照lock mode,数据库对B-Tree操作区分几种类型:

btr_search_leaf
btr_modify_leaf
btr_modify_tree
btr_search_prev
btr_modify_prev

根据这些不同的操作类型,我们下面来分析一下加锁的过程。

场景分析

场景1. 索引扫描查询

如果sql通过索引进行扫描,其latch mode为btr_search_leaf:

首先是hold住index lock的RW_S_LATCH,然后通过btr_cur_search_to_nth_level进行B-Tree查询leaf节点的过程。当cursor定位到leaf节点上之后,在leaf page节点上,添加RW_S_LATCH锁,即S锁,然后通过save_point的mtr释放index lock的S锁。在扫描的过程中,因为持有index的RW_S_LATCH,所以节点的扫描比如root、branch这样的node block,并不持有任何mode的rw lock。直到latch住leaf节点后,就释放掉 index 的锁,这样尽可能的减少阻塞,剩下就是leaf节点的扫描过程,只持有leaf page的锁。 扫描完数据,就释放leaf page的S锁。

场景2. 升序和降序查询

场景2和场景1在持有index lock的过程中,是相同的,都是在search的过程中,持有RW_S_LATCH,一旦定位到leaf page,就释放掉index 的S锁,升序和降序的扫描过程中,会沿着leaf page之间的双链表进行扫描,因为是双向链表,所以可以完成asc和desc的扫描。但这里要注意的是,InnoDB先持有下一个page的lock,然后再释放当前持有page的lock,这样就有可能造成死锁,所以InnoDB不管当前是asc还是desc的扫描,都会先持有左leaf page的lock,然后再持有下一个leaf page的锁,最后释放prev page的lock,这样做到加锁的顺序一致,避免死锁。

场景3. 乐观插入记录

InnoDB在插入记录的过程中,分了两个步骤,乐观插入和悲观插入:
1. 乐观,就是当前leaf page的剩余空间满足记录的插入需要;
2. 悲观,就是需要split B-Tree,增加leaf page来完成新记录的插入。

先看乐观插入:
场景1和场景2都持有leaf page的RW_S_LATCH,但在插入的过程中,操作类型是btr_modify_leaf,需要持有leaf page的RW_X_LATCH, 在search的过程中,和场景1、2相同,都是持有index的RW_S_LATCH lock,一旦定位结束,释放index lock。

场景4. 悲观插入记录

悲观插入,需要split B-Tree,所以首先会持有index lock,mode为RW_X_LATCH,并X lock三个leaf page,即prev,current,next三个leaf page,然后修改branch节点的记录,指向leaf节点,修改完成后,才能释放index lock。
在split的过程中,无法进行search操作(因为正在修改branch节点),但如果其他线程已经在读取leaf page,并不会受影响。

场景5. online DDL

在online DDL的过程中,比如add index,因为是新添加的index,并不会产生并发访问的问题。

场景6. DDL

比如加字段的过程,其并发问题,由server层的MDL锁和InnoDB层的事务锁来完成其同步。

问题:

我们来看上面提到的6个场景,对我们日常使用InnoDB的过程中,影响最大的就是场景4,即split的过程中,会严重的影响并发,因为index 的X lock,导致任何的B-Tree扫描都产生了阻塞。有解吗?

通常我们碰到lock导致的并发问题的时候,第一个想到的就是降低锁对象的粒度,粒度越小,共享区域也就越小,冲突的几率也就越小,并发就能够提高。

根据这个原则,我们回过头来看这个问题,因为index lock 保护了整个B-Tree的结构,但我们对某一个branch节点进行split的时候,我们仅仅修改了这个branch节点,所以我们可以把锁的粒度降低到某些要修改的branch节点上,这样就可以不影响其他branch节点的扫描和访问。

MySQL 5.7的改进

MySQL官方对index lock进行了优化,在split的过程中,尽可能的减少冲突,减少并发的瓶颈。

对于InnoDB的rw lock增加第三种lock mode,即SX锁,其相容性矩阵如下:

  | S|SX| X|
--+--+--+--+
S | o| o| x|
--+--+--+--+
SX| o| x| x|
--+--+--+--+
X | x| x| x|
--+--+--+--+

这里仍然保留了index lock,考虑一下两个存在冲突的场景,还是否阻塞:

1. BTR_SEARCH_LEAF和BTR_MODIFY_LEAF

对于扫描leaf节点和修改leaf节点的场景:

index->lock 持有S锁不变
branch->latch 从无--> S latch
   latch order:
       latch root block (S)
       latch root-1 block (S)
       ....
       latch leaf+1 block (S)
leaf->latch 持有S或者X锁不变
release index lock 不变
release branch latch 从无到释放

和之前的差别是在search的过程中,对使用到的branch节点,加上S锁,用于同步branch节点的修改。同样,当定位到leaf节点后,就可以把index lock和branch lock全部释放掉了,后面leaf节点之间的移动,同样不需要index lock和branch lock。

2. BTR_MODIFY_TREE

对于修改index B-Tree结构的场景:

index->lock 从X锁-->SX 锁
branch->latch 从无--> X latch

注意:因为有index SX锁,所以不允许并发的修改B-Tree操作,所以,只需要X latch要修改的branch即可。

和之前的差别就是index lock从X锁变成了SX锁,这样并不影响search的过程,增加了更改过程中branch节点的X锁。

总结:

这样修改后,index lock在并发的过程中,修改B-Tree和search B-Tree没有了并发冲突问题,在split的过程中,只有search和modify到同一个branch节点,才会产生阻塞,对于我们正常的使用数据库过程中(大部分都是通过index进行读写),可以显著的提升并发能力。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
26天前
|
存储 关系型数据库 MySQL
MySQL 8.0特性-自增变量的持久化
【11月更文挑战第8天】在 MySQL 8.0 之前,自增变量(`AUTO_INCREMENT`)的行为在服务器重启后可能会发生变化,导致意外结果。MySQL 8.0 引入了自增变量的持久化特性,将其信息存储在数据字典中,确保重启后的一致性。这提高了开发和管理的稳定性,减少了主键冲突和数据不一致的风险。默认情况下,MySQL 8.0 启用了这一特性,但在升级时需注意行为变化。
|
27天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
131 11
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
27天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的表空间
InnoDB是MySQL默认的存储引擎,主要由存储结构、内存结构和线程结构组成。其存储结构分为逻辑和物理两部分,逻辑存储结构包括表空间、段、区和页。表空间是InnoDB逻辑结构的最高层,所有数据都存放在其中。默认情况下,InnoDB有一个共享表空间ibdata1,用于存放撤销信息、系统事务信息等。启用参数`innodb_file_per_table`后,每张表的数据可以单独存放在一个表空间内,但撤销信息等仍存放在共享表空间中。
|
27天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的段、区和页
MySQL的InnoDB存储引擎逻辑存储结构与Oracle相似,包括表空间、段、区和页。表空间由段和页组成,段包括数据段、索引段等。区是1MB的连续空间,页是16KB的最小物理存储单位。InnoDB是面向行的存储引擎,每个页最多可存放7992行记录。
|
27天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL的InnoDB存储引擎
InnoDB是MySQL的默认存储引擎,广泛应用于互联网公司。它支持事务、行级锁、外键和高效处理大量数据。InnoDB的主要特性包括解决不可重复读和幻读问题、高并发度、B+树索引等。其存储结构分为逻辑和物理两部分,内存结构类似Oracle的SGA和PGA,线程结构包括主线程、I/O线程和其他辅助线程。
【赵渝强老师】MySQL的InnoDB存储引擎
|
2月前
|
存储 关系型数据库 MySQL
mysql 引擎概述
MySQL存储引擎是处理不同类型表操作的组件,InnoDB是最常用的默认引擎,支持事务、行级锁定和外键。MySQL采用插件式存储引擎架构,支持多种引擎,如MyISAM、Memory、CSV等,每种引擎适用于不同的应用场景。通过`SHOW ENGINES`命令可查看当前MySQL实例支持的存储引擎及其状态。选择合适的存储引擎需根据具体业务需求和引擎特性来决定。
|
2月前
|
SQL 安全 关系型数据库
MySQL8.2有哪些新特性?
【10月更文挑战第3天】MySQL8.2有哪些新特性?
47 2
|
2月前
|
存储 关系型数据库 MySQL
mysql 8.0 的 建表 和八种 建表引擎实例
mysql 8.0 的 建表 和八种 建表引擎实例
26 0
|
7天前
|
存储 关系型数据库 MySQL
MySQL引擎InnoDB和MyISAM的区别?
InnoDB是MySQL默认的事务型存储引擎,支持事务、行级锁、MVCC、在线热备份等特性,主索引为聚簇索引,适用于高并发、高可靠性的场景。MyISAM设计简单,支持压缩表、空间索引,但不支持事务和行级锁,适合读多写少、不要求事务的场景。
30 9
|
6月前
|
存储 关系型数据库 MySQL
MySQL数据库进阶第六篇(InnoDB引擎架构,事务原理,MVCC)
MySQL数据库进阶第六篇(InnoDB引擎架构,事务原理,MVCC)

相关产品

  • 云数据库 RDS MySQL 版