MySQL的锁
MySQL的锁
- 原则 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);
案例一:等值查询间隙锁
由于表t没有id=7这条记录,所以按照上面的规则判断一下如何加锁:
- 根据原则1,加锁单位是next-key lock,所以session A的加锁范围就是(5,10];
- 再更具优化2,这是一个等值查询 id=7,但是查不到,所以退化为间歇锁,加锁范围为(5,10)。
而8刚好是在(5,10)这个间歇锁的范围内,所以session B的插入语句会被阻塞,而session C的插入语句不会被阻塞,10不在(5,10)的区间内。
案例二:非唯一索引等值锁
这个例子是关于覆盖索引上的锁
首先session A要给索引c上c=5这一行加上读锁。
- 根据原则1,加锁单位是next-key lock,因此会给(0,5]加上next-key lock。
- 因为c是普通索引,不保证唯一性,所以只找到c=5的时候还不可以停下来,根据原则二,访问到的都得加锁,那么10被访问到了,10也得加锁,所以区间为(5,10]。
- 根据优化2,等值判断,向右一直遍历到不满足的时候位置,然后next-key lock退化为间歇锁,因此,锁的区间变为(5,10)。
- 这个查询用到了覆盖索引,所以不需要回表,那么主键索引树上也就不会加锁了
为什么session B可以update成功?
访问路径:
- 这条语句通过主键
id
定位到记录id=5
,直接访问主键索引。 - 因为
session A
没有加锁主键索引,session B
可以直接修改该行数据。
session A
的锁范围:
- 锁范围只限于索引
c
上的(0,5]
和(5,10)
。 session A
没有锁住主键索引上的记录id=5
。
为什么session C会被阻塞呢?
session C
执行:
INSERT INTO t VALUES (7,7,7);
访问路径:
- 插入新记录
(7,7,7)
会尝试在索引c
中找到插入位置。 - 索引
c
的(5,10)
范围已经被session A
加了Gap Lock
。
- 插入新记录
锁冲突:
- Gap Lock 阻止了任何事务在
(5,10)
范围内插入新记录。 - 因此,
session C
被阻塞。
- Gap Lock 阻止了任何事务在
版权申明
本文系作者 @hayaizo 原创发布在Hello World站点。未经许可,禁止转载。
暂无评论数据