MySQL探秘(七):InnoDB行锁算法

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 在上一篇《InnoDB一致性非锁定读》中,我们了解到InnoDB使用一致性非锁定读来避免在一般的查询操作(SELECT FOR UPDATE等除外)时使用锁。然而锁这个事情是无法避免的,数据的写入,修改和删除都需要加锁。今天我们就继续学习InnoDB锁相关的知识。

 在上一篇《InnoDB一致性非锁定读》中,我们了解到InnoDB使用一致性非锁定读来避免在一般的查询操作(SELECT FOR UPDATE等除外)时使用锁。然而锁这个事情是无法避免的,数据的写入,修改和删除都需要加锁。今天我们就继续学习InnoDB锁相关的知识。

 由于文章涉及的概念比较多,害怕大家看完后会骂人,有一种字我都认识,就不太懂的感觉,文章会给出一些实例和试验,依据具体案例来讲解这些概念。毕竟,实践才能出真知。

 InnoDB存储引擎支持表锁和行锁。顾名思义,表锁是锁住整张表,行锁只是锁住某些行。InnoDB通过给索引项加锁来实现行锁,如果没有索引,则通过隐藏的聚簇索引来对记录加锁。如果操作不通过索引条件检索数据,InnoDB 则对表中的所有记录加锁,实际效果就和表锁一样。InnoDB存储引擎有3种行锁的算法,分别是:

  • Record Lock: 单个记录上的锁
  • Gap Lock: 间隙锁,锁定一个范围,但不包括记录本上
  • Next-Key Lock: Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身

 如下图所示,

三种锁算法
三种锁算法

 例如一个索引有10,11,13,20这四个值。InnoDB可以根据需要使用Record Lock将10,11,13,20四个索引锁住,也可以使用Gap Lock将(-∞,10),(10,11),(11,13),(13,20),(20, +∞)五个范围区间锁住。Next-Key Locking类似于上述两种锁的结合,它可以锁住的区间有为(-∞,10],(10,11],(11,13],(13,20],(20, +∞),可以看出它即锁定了一个范围,也会锁定记录本身。

 InnoDB存储引擎的锁算法的一些规则如下所示,后续章节会给出对应的实验案例和详细讲解。

  • 在不通过索引条件查询时,InnoDB 会锁定表中的所有记录。所以,如果考虑性能,WHERE语句中的条件查询的字段都应该加上索引。

  • InnoDB通过索引来实现行锁,而不是通过锁住记录。因此,当操作的两条不同记录拥有相同的索引时,也会因为行锁被锁而发生等待。

  • 由于InnoDB的索引机制,数据库操作使用了主键索引,InnoDB会锁住主键索引;使用非主键索引时,InnoDB会先锁住非主键索引,再锁定主键索引。

  • 当查询的索引是唯一索引(不存在两个数据行具有完全相同的键值)时,InnoDB存储引擎会将Next-Key Lock降级为Record Lock,即只锁住索引本身,而不是范围。

  • InnoDB对于辅助索引有特殊的处理,不仅会锁住辅助索引值所在的范围,还会将其下一键值加上Gap LOCK。

  • InnoDB使用Next-Key Lock机制来避免Phantom Problem(幻读问题)。

真的了解本质吗?

 在不通过索引条件查询时,InnoDB 会锁定表中的所有记录。大家可以登录上自己的MySQL服务器,亲自试验一下。

示例一
示例一

 试验发现,会话二的查询操作真的是会发生等待。那么,这句话真的是对的吗?我们可以使用《InnoDB锁的类型和状态查询》中查询数据锁的方法查询一下,注意必须在会话二操作还在等待时进行查询,否则查询不到

查询锁信息
查询锁信息

 其中lock_trx_id为1851的事务是会话二的事务,另一个是会话一的事务。我们可以看到两个锁都要对值为1的主键索引加锁。需要注意的是,这里是对主键进行加锁。二者之间的关系是怎么确定的呢?我们可以通过information_schema.INNODB_LOCK_WAITS中的数据确定。

 奇怪,不是说好的锁定表中的所有记录嘛?查找了很多资料,发现INNODB_LOCKS的定义如下:

The INNODB_LOCKS table contains information about each lock that an InnoDB transaction has requested but not yet acquired, and each lock that a transaction holds that is blocking another transaction.

 也就是说,这张表并不会显示所有锁的信息,而是只显示要申请却没有申请到,和已经持有锁并且阻塞其他线程的锁信息。怪不得必须在会话二进行等待时进行查询才能查得到数据。

 因为两个会话的操作都要锁住所有的行,所以发现每次在第一行记录上就发生了锁等待。那我们使用插入语句试试。表e1的主键a的值为1-4,我们分别插入主键为1-4(当然会有主键重复问题,但是由于有锁,一直等待)的新记录,分别查询锁信息,就能看到会话一的事务对所有的主键都加了锁,也就是对所有的记录都加了锁。

是索引,而不是记录

 InnoDB存储引擎的行锁是通过锁住索引实现的,而不是记录。这是理解很多数据库锁问题的关键。

 由于InnoDB特殊的索引机制,数据库操作使用主键索引时,InnoDB会锁住主键索引;使用非主键索引时,InnoDB会先锁住非主键索引,再锁定主键索引。不了解InnoDB索引机制的可以参考这篇文章

 如下图所示,当InnoDB锁定非主键索引b时,它也会锁住其对应的主键索引,所以锁住b值为2和3的非主键索引,那么与其相关的a值为6,5的主键索引也需要被锁住。

非主键索引的加锁
非主键索引的加锁

 比如说,一种常见的死锁情况一般出现在如下图所示的操作场景中。

示例2
示例2

 会话一的语句使用了b上的索引,因为它是非主键索引,所以会先在b索引上添加锁,再去a索引上加锁。而会话二的语句恰恰相反,会先在索引a上加锁,再去索引b加锁。这种情况下,就可能出现死锁。

Next-Key Lock锁到底有什么用?

 默认隔离级别REPEATABLE-READ下,InnoDB中行锁默认使用算法Next-Key Lock,只有当查询的索引是唯一索引或主键时,InnoDB会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。当查询的索引为辅助索引时,InnoDB则会使用Next-Key Lock进行加锁。InnoDB对于辅助索引有特殊的处理,不仅会锁住辅助索引值所在的范围,还会将其下一键值加上Gap LOCK。

 废话不多说,我们来看一下相关的实验,先做一下准备。

CREATE TABLE e4 (a INT, b INT, PRIMARY KEY(a), KEY(b));
INSERT INTO e4 SELECT 1,1;
INSERT INTO e4 SELECT 3,1;
INSERT INTO e4 SELECT 5,3;
INSERT INTO e4 SELECT 7,6;
INSERT INTO e4 SELECT 10,8;

 然后开启一个会话执行下面的语句。

SELECT * FROM e4 WHERE b=3 FOR UPDATE

 因为通过索引b来进行查询,所以InnoDB会使用Next-Key Lock进行加锁,并且索引b是非主键索引,所以还会对主键索引a进行加锁。对于主键索引a,仅仅对值为5的索引加上Record Lock(因为之前的规则)。而对于索引b,需要加上Next-Key Lock索引,锁定的范围是(1,3]。除此之外,还会对其下一个键值加上Gap Lock,即还有一个范围为(3,6)的锁。
大家可以再新开一个会话,执行下面的SQL语句,会发现都会被阻塞。

SELECT * FROM e4 WHERE a = 5 FOR UPDATE;  # 主键a被锁
INSERT INTO e4 SELECT 4,2;   # 插入行b的值为2,在锁定的(1,3]范围内
INSERT INTO e4 SELECT 6,5# 插入行b的值为5,在锁定的(3,6)范围内

 InnoDB引擎采用Next-Key Lock来解决幻读问题。因为Next-Key Lock是锁住一个范围,所以就不会产生幻读问题。但是需要注意的是,InnoDB只在Repeatable Read隔离级别下使用该机制。

后记

 我们后续还会探讨InnoDB的事务的知识,请大家持续关注。

参考

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
115 11
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
9天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的表空间
InnoDB是MySQL默认的存储引擎,主要由存储结构、内存结构和线程结构组成。其存储结构分为逻辑和物理两部分,逻辑存储结构包括表空间、段、区和页。表空间是InnoDB逻辑结构的最高层,所有数据都存放在其中。默认情况下,InnoDB有一个共享表空间ibdata1,用于存放撤销信息、系统事务信息等。启用参数`innodb_file_per_table`后,每张表的数据可以单独存放在一个表空间内,但撤销信息等仍存放在共享表空间中。
|
9天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的段、区和页
MySQL的InnoDB存储引擎逻辑存储结构与Oracle相似,包括表空间、段、区和页。表空间由段和页组成,段包括数据段、索引段等。区是1MB的连续空间,页是16KB的最小物理存储单位。InnoDB是面向行的存储引擎,每个页最多可存放7992行记录。
|
9天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL的InnoDB存储引擎
InnoDB是MySQL的默认存储引擎,广泛应用于互联网公司。它支持事务、行级锁、外键和高效处理大量数据。InnoDB的主要特性包括解决不可重复读和幻读问题、高并发度、B+树索引等。其存储结构分为逻辑和物理两部分,内存结构类似Oracle的SGA和PGA,线程结构包括主线程、I/O线程和其他辅助线程。
【赵渝强老师】MySQL的InnoDB存储引擎
|
1月前
|
存储 缓存 关系型数据库
详细解析MySQL中的innodb和myisam
总之,InnoDB和MyISAM各有千秋,选择合适的存储引擎应基于对应用程序特性的深入理解,以及对性能、数据完整性和可扩展性的综合考量。随着技术发展,InnoDB因其全面的功能和日益优化的性能,逐渐成为更广泛场景下的首选。然而,在特定条件下,MyISAM依然保留其独特的价值。
116 0
|
3月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
255 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
3月前
|
监控 关系型数据库 MySQL
在Linux中,mysql的innodb如何定位锁问题?
在Linux中,mysql的innodb如何定位锁问题?
|
3月前
|
SQL 存储 关系型数据库
"MySQL增列必锁表?揭秘InnoDB在线DDL,让你的数据库操作飞一般,性能无忧!"
【8月更文挑战第11天】在数据库领域,MySQL凭借其稳定高效的表现深受开发者喜爱。对于是否会在给数据表添加列时锁表的问题,MySQL的行为受版本、存储引擎等因素影响。从5.6版起,InnoDB支持在线DDL,可在改动表结构时保持表的可访问性,避免长时间锁表。而MyISAM等则需锁表完成操作。例如,在使用InnoDB的表上运行`ALTER TABLE users ADD COLUMN email VARCHAR(255);`时,通常不会完全锁表。虽然在线DDL提高了灵活性,但复杂操作或大表变更仍可能暂时影响性能。因此,进行结构变更前应评估其影响并择机执行。
71 6
|
3月前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现'1.20.0'在'1.10.0'之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于'major.minor.patch'格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
190 3
|
26天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。

热门文章

最新文章