Mysql锁之——行锁

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Mysql锁之——行锁

这篇文章我们来聊聊Mysql的行锁

InnoDB的行锁包括:记录锁、间隙锁(Gap Lock,解决幻读问题)和组合锁。

两阶段锁协议

在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

行锁

顾名思义,就是给一行数据记录上锁。

间隙锁

注意:以下所有例子都是默认在RR隔离级别下:

// 创建表 t 
mysql> CREATE TABLE `t`(
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)  
)ENGINE=InnoDB;
// 插入记录
mysql> INSERT INTO t values(0,0,0),(5,5,5),(10,10,10),(15,15,15),
    (20,20,20),(25,25,25);

假设,只给d=5这条记录上锁,执行以下语句:

mysql> select * from t where d=5 for update;

什么是幻读?如上图:

  • 事务A 执行select * from t where d=5 for update,查询d=5的所有记录
  • 事务B将id=0的d字段修改5,事务A查询到两条结果
  • 事务C插入一条记录,也满足事务A的查询条件,事务A查询到三条结果。

幻读

  • 在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,“幻读”只有在当前读下才会出现;
  • 幻读专指新插入的行。

解决

幻读产生的原因是,行锁只能锁住行,而插入语句是要插入到现有数据记录的“间隙”。因此,为了解决幻读,InnoDB引入了间隙锁

间隙就是两个值之间的间隙,如开头创建的表t,插入了6条记录,则产生7个间隙,都是开区间,如下图:

分析:select * from t where d=5 for update;

实际上,因为d上没有索引,所以会走全表扫描,在一行一行的扫描过程中,不仅给每行加上锁,而且给两个值之间的间隙也加了间隙锁,这样就可以确保无法插入新的数据记录。

组合锁(Next-key lock)

就是间隙锁+行锁,前开后闭区间。如:(5,10], (10, 15],(15, 20],(20, 25]。

加锁规则

  1. 加锁的单位是next-key-lock;
  2. 只有访问到的对象才会加锁;
  3. 对于唯一索引的等值查询来说,next-key-lock会退化为行锁;
  4. 索引的等值查询来说,向右遍历时,右边界不满足等值条件时,next-key-lock会退化为间隙锁;
  5. 对于唯一索引的范围查询来说,会访问到第一个不满足条件的记录为止。

例一:主键索引等值锁

分析:

  • 根据规则1,Session A会在主键id上 加 (5,10] 的 next-key lock;
  • 因为Session A的next-key lock,Session B的插入会进入锁等待,直至超时;
  • 根据规则4,向右遍历的右边界10,不满足等值条件id=7,所以next-key lock退化为间隙锁(5,10),所以,Session C的更新可以成功。

例二:覆盖索引等值锁

分析:这里 session A 要给索引 c 上 c=5 的这一行加上读锁。

  • 根据规则1,会在索引c上加(0,5] 的 next-key lock;
  • 因为c是普通索引,到c=5这条记录不会停下来,会继续往后扫描,直至不满足等值条件,也就是 c=10 为止。根据规则2,还会在索引c上加(5,10]的next-key lock;
  • 根据规则4,继续向后遍历,右边界10不满足等值条件,该next-key lock退化为间隙锁(5,10);
  • 根据规则2,只有在访问到的对象才会加锁。注意此时的查询用到了覆盖索引,并不需要访问主键索引,所以主键索引上没有任何锁。所以Session B的update语句可以执行成功。但是Session C的插入语句会被 Session A 的间隙锁锁住。

注意,这个例子中是以 lock in share mode 方式加锁,这种加锁方式默认不会修改数据,只会锁覆盖索引。如果是以 for update 方式,系统会认为你接下来会修改数据,顺便就主键索引上满足条件的行加上行锁。

例三:主键索引范围锁

先来看看,以下两条语句有什么不同吗?

mysql> select * from t where id=10 for update;
mysql> select * from t where id>=10 and id<11 for update;

对于整型字段来说,上述两条语句在逻辑上执行结果是一致的,但是加锁的范围却不同。下面,我们来看看第二条语句的加锁效果:

分析:

  • 根据规则1,会在主键索引id上加(5,10] 的 next-key lock;
  • 根据规则3,唯一索引的next-key lock会退化为行锁,也就是只加了id=10的行锁;
  • 因为是范围查询,继续向后扫描,找到id=15停下来,根据规则2,会在id上加(10,15] 的next-key lock;

所以,Session A这时候的锁范围就在主键索引id=10的行锁,以及(10,15]的next-key lock;这样SessionB 和 C的执行结果你就理解了。

例四:非唯一索引范围锁

与例子三不同的是,用的是非唯一索引c。

select * from t where c>=10 and c<11 for update;

分析:

  • 根据规则1,会在索引c上加(5,10] 的next-key lock;
  • 因为c不是唯一索引,所以规则3不适用。
  • 因为范围查询,继续向后扫描,找到c=15停下来。根据规则2,会在c上加(10,15] 的next-key lock;

所以,Session A就加了(5,10] 和 (10,15] 的next-key lock; 那么此时主键索引上有那些锁呢?我们继续来验证:

可以看到,主键id=10的记录被加了锁。

例五:唯一索引范围锁Bug

mysql> select * from t where id >10 and id <=15 for update;

分析:

  • 根据规则1,会在id索引上加(10,15] 的 next-key lock;因为id是唯一键,所以到id=15这就结束了。但是,InnoDB 会继续往后扫描,直到第一个不满足条件的记录为止,也就是id=20。所以,根据规则2,就需要给id索引加上(15,20] 的next-key lock。

所以,Session B 对于id=20行记录的update会被阻塞,同样,Session C的插入语句也会被阻塞。

例六:非唯一索引上的“等值”的例子

先给表t插入一行记录

mysql> insert into t values(30, 10, 30);

新插入的行数据c=10,此时表中就有两条c=10 的记录,分别是(10,10,10)和 (30,10,30)。但是,这两条记录的主键值不同,也就是说,这两个c=10的记录中间也有间隙。

接下来我们来看例六:

分析:

  • 根据规则1,会在索引c上加(5,10] 的 next-key lock;
  • 因为是非唯一索引,会继续往后扫描,找到下一个c=10&&id=30的记录,继续往后扫描,直到不满足等值条件id=15时停止,根据规则2,加上(10,15] 的 next-key lock;
  • 因为是等值查询,根据规则4,向右扫描,右边界15不满足等值c=10条件,next-key lock退化为间隙锁(10,15);

所以,Session B被阻塞,Session C执行成功。加锁范围如下:

例七:limit语句

分析:

虽然,你知道只有两条满足条件的记录,两个语句的执行逻辑是一致的,但是与例六不同的是,加了limit 2限定,在找到两条c=10的记录后,就不再往后扫描了,所以加锁范围也有了不同,如下:

例八:死锁的例子

分析:

  • Session A给索引c,加了(5,10] 的next-key lock,和 (10,15)的间隙锁;
  • Session B也要加(5,10] 的 next-key lock,阻塞等待;
  • Session A的插入也被阻塞;

很好的说明了,next-key lock等价于 “行锁 + 间隙锁”。实际上,Session B的加锁是分为两步的,先加(5,10)的间隙锁,间隙锁和间隙锁不冲突,添加间隙锁成功,再去申请行锁的时候,阻塞了。从而导致了Session A 往这个间隙的插入操作阻塞了,形成死锁,InnoDB 让事务B回滚。

补充知识:

可重复读隔离级别下,普通的查询(select … from t where …;)是快照读,不会加锁,也就是利用MVCC读取数据。

当前读:select…for update 是当前读,当前读就是能读到所有已提交的记录的最新值。

加锁介绍

select … where…lock in share mode,这种方式会加S共享锁

select…where…for update,这种方式会加X排它锁,

如果是update、delete都会加X排他锁,具体这个锁是加在聚簇索引还是二级索引,以及对应索引的哪些记录上,由过滤条件:1.是哪种索引(主键索引、唯一索引、非唯一索引);2.事务的隔离级别来决定。

X锁 加锁过程

  1. 主键索引:在符合条件的记录上加X锁
  2. 非主键唯一索引:在符合条件的唯一辅助索引的索引记录上加X锁,并在对应的主键索引的记录上加X锁,若是查询用到了覆盖索引,并且使用的lock in share mode则不会给主键索引对应的记录加锁。
  3. 非唯一索引:
    (RC)符合条件的索引记录上都加X锁,对应的主键索引上也加X锁
    (RR)符合条件的索引记录上都加X锁,对应的聚簇索引上也加X锁,此外,二级索引符合条件的前后的间隙都会加上间隙锁,防止幻读。
  4. 无索引:
    (RC)主键索引的所有记录都会加X锁
    (RR)主键索引的所有记录都会加X锁,而且所有的间隙都会加间隙锁。

笔记参考于极客时间《MySQL实战45讲》


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
53 3
|
1月前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
68 1
|
1月前
|
关系型数据库 MySQL Java
MySQL数据锁:Record Lock,Gap Lock 和 Next-Key Lock
本文基于 MySQL 8.0.30 版本及 InnoDB 引擎,深入解析三种行锁机制:记录锁(Record Lock)、间隙锁(Gap Lock)和临键锁(Next-key Lock)。记录锁锁定索引记录,确保事务唯一修改;间隙锁锁定索引间的间隙,防止新记录插入;临键锁结合两者,锁定范围并记录自身,有效避免幻读现象。通过具体示例展示了不同锁的作用机制及其在并发控制中的应用。
149 2
|
1月前
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
58 1
|
2月前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
294 5
|
1月前
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
63 0
|
1月前
|
关系型数据库 MySQL 数据库
mysql锁详解
通过理解并合理运用MySQL中的锁机制,开发者可以有效管理数据库并发访问,平衡性能与数据一致性需求。更多关于MySQL锁的深入探讨和最佳实践,请参考专业的数据库管理资源[[深入MySQL锁机制详解
40 0
|
2月前
|
关系型数据库 MySQL 数据库
Mysql的锁
本文介绍了MySQL中表级锁和行级锁的区别,其中MyISAM仅支持表级锁,而InnoDB支持表级锁和行级锁,默认为行级锁。表级锁锁定整个表,实现简单,资源消耗少,但并发度低;行级锁仅锁定相关记录,减少冲突,提高并发度,但加锁开销大。此外,还介绍了共享锁和排他锁的概念及意向锁的作用。
|
2月前
|
存储 SQL 关系型数据库
MySQL 的锁机制,那么多的锁,该怎么区分?
MySQL 的锁机制,那么多的锁,该怎么区分?
43 0
|
3月前
|
关系型数据库 MySQL 数据库
MySQL MVCC和间隙锁有什么区别?
【8月更文挑战第24天】MySQL MVCC和间隙锁有什么区别?
100 0

热门文章

最新文章

下一篇
无影云桌面