MySQL的加锁规则

此次的内容是学习极客时间的MySQL实战45讲课程中的实验和总结,具体课程是第21篇文章。sql

首先是课程中的总结的加锁规则,两个“原则”、两个“优化”和一个“bug”(可重复读的事务隔离级别下)。学习

原则 1:加锁的基本单位是 next-key lock。但愿你还记得,next-key lock 是前开后闭区间。

原则 2:查找过程当中访问到的对象才会加锁。

优化 1:索引上的等值查询,给惟一索引加锁的时候,next-key lock 退化为行锁。

优化 2:索引上的等值查询,向右遍历时且最后一个值不知足等值条件的时候,next-key lock 退化为间隙锁。

一个 bug:惟一索引上的范围查询会访问到不知足条件的第一个值为止。  

而后是此次用到的表和数据优化

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;

insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

再而后就是实际例子了3d

1.在主键上,访问不存在的数据

根据原则1加的next-key lock锁是id(5,10],由于id等于7的数据并不存在,因此不知足优化1,根据优化2将会退化为间隙锁id(5,10),从上面的实际例子中也能看出来,只有插入id=6的数据在等待锁,id=四、十一、十、5的数据在插入和更新的时候都正常。对象

2.在主键上,访问存在的数据

 

 

 这个和上一个例子相似区别是知足优化1因此就从next-key lock锁id(5,10],退化为id=5的行锁,因此id=6,4的数据能够插入进去,只有id=5的数据在更新的时候等待锁。blog

3.在主键上 范围查询时会根据扫描的状况加锁

 

 

 这种状况就是索引

  • 根据原则1加的是next-key lock锁id(0,5],
  • 根据bug知道扫描到了10因此还加了next-key lock锁id(0,10]
  • 再而后根据优化2,由于这里有等值查询(id=5),因此next-key lock锁id(0,5]退化成了id=5行锁

因此id=四、11的数据能够插入,而id=六、10的数据的更新和插入的时候须要等待锁事务

4.在主键上 范围查询时会根据扫描的状况加锁2

 

 

分析:it

  • 根据原则1加上了id(5,10],id(10,15]的next-key lock锁
  • 根据优化1id(10,15]的next-key lock锁退化成行锁id=10

可是实际状况上id=11的数据插入也须要等待锁,这就是上面规则说的bug惟一索引上的范围查询会访问到不知足条件的第一个值为止,这里的id<=10,会一直扫描到id=15,因此会加上(10,15]的next-key lock锁class

5.在非惟一索引上等值查询的状况

这实际上是两个例子,由于只有一个区别就放一块儿了,先按照规则来分析

  • 根据原则1就能知道都加上了c(0,5],c(5,10]的next-key lock锁
  • 根据优化2c(5,10]的next-key lock锁会退化为c(5,10)的间隙锁

这两个加锁的区别就是对于id=5的锁的问题,lock in share mode没有对id=5加锁,因此能够获得的信息是,lock in share mode只锁覆盖索引,可是若是是 for update 就不同了。 执行 for update 时,系统会认为你接下来要更新数据,所以会顺便给主键索引上知足条件的行加上行锁。

6.在非惟一索引上范围查询的状况

 

 

 这个和3例子相似,区别是这里不是主键,也就是说不是惟一索引,因此这里的分析过程就是

  • 根据原则1加上了c(0,5],c(5,10]的next-key lock锁
  • 由于不是惟一索引不知足优化1,因此c(0,5]不会退化成c=5的行锁,因此最终结果就是c(0,5],c(5,10]

7.在非惟一索引范围查询时 会根据扫描的状况加锁

 

 

 这个例子和第5个例子相似,区别就是limit的问题,由于规则中总结的bug能够解释,limit 1只须要扫描一行,因此c(5,10)的间隙锁就没加上,而limit 2就再次证实了,若是须要返回两条数据须要再扫描就加上了c(5,10)的间隙锁

 

 

 没有order by的状况先来分析:

  • 根据原则1c(5,10]、c(10,15]、c(15,20]都加上了next-key lock锁
  • 根据优化2c(15,20]退化为间隙锁,c(15,20)
  • 根据bugc<=15会一直扫描到20,因此c(10,15]的锁仍是会加上
  • 因此最终结果是c(5,10]、c(10,15]、c(15,20]都加上了锁

而后是有order by desc的状况:

  • 由于order by因此第一个定位的是索引上c=15的数据行,因此会加上c(10,15]的next-key lock锁和c(15,20)的间隙锁
  • 由于向左扫描,扫描到了c=5才停下来,因此加上了c(0,5]、c(5,10]的next-key lock锁
  • 由于c=五、c=十、c=15都有值,且select id,因此这三行的id索引上加了3个行锁
  • 因此最终状况是c(5,20),id=五、十、15的锁

总结

这上面总结的规则,其实没啥道理可言,MySQL代码就是这样写的,加锁的意义是实际上为了保持数据的一致性和语义的正确,从例子4咱们就能看到明明咱们想锁的是5-10,可是由于bug(10,15]也加上了锁。因此考虑MySQL的这个加锁问题的时候,最好仍是根据扫描的状况来考虑

而后是一些细节的记录:

  • <=a 怎么判断是间隙锁仍是行锁,这个要看扫描的过程来看,要先找到这个a,这个用等值判断来断定,而后在索引中扫描的过程使用范围查找来判断,就像最后一个例子中的order by desc同样,向左向右扫描出现的状况也不同
  • 加锁的状况其实不单单是经过for update 和lock in share mode来决定的,还得看查询的结果,若是是select * 查到了主键,同样会锁主键索引,这和访问的对象有关,访问到了就加锁,就像扫描同样
  • 有行才会加行锁,若是查询没有命中行就加next-key lock锁,而后若是是等值查询还须要根据优化2来判断怎么加间隙锁
相关文章
相关标签/搜索