两万字详解InnoDB的锁( 六 )


两万字详解InnoDB的锁

文章插图
 
3.9 Serializable串行化在Serializable串行化的隔离级别下,对于写的语句,比如update account set balance= balance-10 where name=‘Jay’;,跟RC和RR隔离级别是一样的 。不一样的地方是,在查询语句,如select balance from account where name = ‘Jay’;,在RC和RR是不会加锁的,但是在Serializable串行化的隔离级别,即会加锁 。
如文章开始第一小节的那个例子,就是类似的:
两万字详解InnoDB的锁

文章插图
 
4. RR隔离级别下,加锁规则到底是怎样的呢?对于RC隔离级别,加的排他锁(X锁),是比较好理解的,哪里更新锁哪里 。但是RR隔离级别,间隙锁是怎么加的呢?我们一起来学习一下 。
对InnoDb的锁来说,面试的时候问的比较多,就是Record lock、Gap lock、Next-key lock 。接下来我们来学习,RR隔离级别,到底一个锁是怎么加上去的 。丁奇的MySQL45讲有讲到,RR隔离级别,是如何加锁的 。大家有兴趣可以去订购看下哈 。非常不错的课程
首先MySQL的版本,是5.x 系列 <=5.7.24,8.0 系列 <=8.0.13 。加锁规则一共包括:两个原则、两个优化和一个bug 。
  • 原则1:加锁的基本单位都是next-key lock 。next-key lock(临键锁)是前开后闭区间 。
  • 原则2:查找过程中访问到的对象才会加锁 。
  • 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁(Record lock) 。
  • 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁(Gap lock) 。
  • 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止 。
假设有表结构和数据如下:
CREATE TABLE t5 ( id int(11) NOT NULL, c int(11) DEFAULT NULL, d int(11) DEFAULT NULL, PRIMARY KEY (id), KEY c (c)) ENGINE=InnoDB;insert into t5 values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);分7个案例去分析哈:
  1. 等值查询间隙锁
  2. 非唯一索引等值锁
  3. 主键索引范围锁
  4. 非唯一索引范围锁
  5. 唯一索引范围锁 bug
  6. 普通索引上存在"等值"的例子
  7. limit 语句减少加锁范围
4.1 案例一:等值查询间隙锁我们同时开启A、B、C三个会话事务,如下:
两万字详解InnoDB的锁

文章插图
 
发现事务B会阻塞等待,而C可以执行成功 。如下:
两万字详解InnoDB的锁

文章插图
 
为什么事务B会阻塞呢?
  • 这是因为根据加锁原则1:加锁基本单位是next-key lock,因此事务Session A的加锁范围是(5,10],这里为什么是区间(5,10],这是因为更新的记录,所在的表已有数据的区间就是5-10哈,又因为next-key lock是左开右闭的,所以加锁范围是(5,10] 。
  • 同时根据优化 2,这是一个等值查询 (id=6),而id=10不满足查询条件 。所以next-key lock退化成间隙Gap锁,因此最终加锁的范围是(5,10) 。
  • 然后事务Session B中,你要插入的是9,9在区间(5,10)内,而区间(5,10)都被锁了 。因此事务B会阻塞等到 。
为什么事务C可以正常执行呢?
这是因为锁住的区间是(5,10),没有包括10,所以事务C可以正常执行 。
4.2 案例二:非唯一索引等值锁按顺序执行事务会话A、B、C,如下:
两万字详解InnoDB的锁

文章插图
 
发现事务B可以执行成功,而C阻塞等待 。如下:
两万字详解InnoDB的锁

文章插图
 
为什么事务会话B没有阻塞,事务会话C却阻塞了?
事务会话A执行时,会给索引树c=5的这一行加上读共享锁 。
  1. 根据加锁原则1,加锁单位是next-key lock,因此会加上next-key lock(0,5] 。
  2. 因为c 只是普通索引,所以仅访问c=5这一条记录时不会马上停下来,需要继续向右遍历,查到c=10才结束 。根据加锁原则2,访问到的都要加锁,因此要给(5,10]加next-key lock 。
  3. 由加锁优化2:等值判断,向右遍历,最后一个值10不满足c=5 这个等值条件,因此退化成间隙锁 (5,10) 。


    推荐阅读