暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MYSQL-note17, 加锁规则

原创 seven 2023-06-29
160


加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”。

原则 1:加锁的基本单位是 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);

 

 

案例一:等值查询间隙锁

Update t set d=d+1 where id = 7;

分析:

使用原则1,由于不存在id=7的记录,所以加锁的是 (5,10】。使用优化2,由于是索引上的等值查询,向右遍历到10 的时候,退化为间隙锁 (5,10)

如果插入记录id=8,则会被lock,而插入id=10,则被允许

 

案例二:非唯一索引等值锁

Select id from t where c=5 lock in share mode

分析:

使用原则1,加锁单位是next-key lock,所以(0,5】会被加锁。使用原则4,由于是索引是索引上的等值查询,向右遍历到10的时候,退化为间隙锁(5,10);使用原则2,只有访问到的对象才会加锁,而这条查询语句使用的是覆盖索引,并不会访问主键索引,所以主键索引不会被锁住

在这个例子中,lock in share mode 只锁覆盖索引,但是如果是 for update 就不一样了。

执行 for update 时,系统会认为你接下来要更新数据,因此会顺便给主键索引上满足条件的行加上行锁。

 

 

案例三:主键索引范围锁

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

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

分析:

第一句的加锁范围,使用原则1,加锁范围(5,10】,使用优化1,加锁范围退化为行锁10

第二句的加锁范围

开始执行的时候,要找到第一个 id=10 的行,因此本该是 next-key lock(5,10]。根据优化 1, 主键 id 上的等值条件,退化成行锁,只加了 id=10 这一行的行锁。

范围查找就往后继续找,找到 id=15 这一行停下来,因此需要加 next-key lock(10,15]

所以这时候锁的范围就是主键索引上,行锁 id=10 和 next-key lock(10,15]

 

案例四:非唯一索引范围锁

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

分析:

由于c是非唯一索引,所以使用优化2,应该是(5 10】(10,15】

 

 

案例六:非唯一索引上存在"等值"的例子

 

接下来的例子,是为了更好地说明“间隙”这个概念。给表 t 插入一条新记录。

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

新插入的这一行 c=10,也就是说现在表里有两个 c=10 的行。

虽然有两个 c=10,但是它们的主键值 id 是不同的(分别是 10 和 30),因此这两个 c=10 的记录之间,也是有间隙的。

 

Delete from t where c=10


分析这条语句的加锁规则

先访问第一个 c=10 的记录。同样地,根据原则 1,这里加的是 (c=5,id=5) 到 (c=10,id=10) 这个 next-key lock。

向右查找,直到碰到 (c=15,id=15) 这一行,循环才结束。根据优化 2,这是一个等值查询,向右查找到了不满足条件的行,所以会退化成 (c=10,id=10) 到 (c=15,id=15) 的间隙锁

综合起来就是  (c=5,id=5) 到(c=15,id=15),左右都是开区间

 

 

案例七:limit 语句加锁

Delete from t where c=10 limit =2

执行结果是一样的,但是加锁规则上,delete 语句明确加了 limit 2 的限制,因此在遍历到 (c=10, id=30) 这一行之后,满足条件的语句已经有两条,循环就结束了。

综合起来就是 (c=5,id=5)到 (c=10,id=30),左开右闭,相比上一个案例,少加锁了(c=10,id=30)到(c=15,id=15)之间的间隙。所以delete的时候尽量加上limit,让操作更安全,还可以减小加锁的范围

 

案例八:一个死锁的例子

目的是说明:next-key lock 实际上是间隙锁和行锁加起来的结果。

Update t set d =d+1 where c=10

update 语句也要在索引 c 上加 next-key lock(5,10],这个锁是分成2步

实际上分成了两步,先是加 (5,10) 的间隙锁,然后加 c=10 的行锁。有可能间隙锁加锁成功,但是行锁加锁失败的情况。再遇到其他加锁冲突的时候,是极有可能造成死锁的

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论