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. 根据原则1,加锁单位是next-key lock,所以session A的加锁范围就是(5,10];
  2. 再更具优化2,这是一个等值查询 id=7,但是查不到,所以退化为间歇锁,加锁范围为(5,10)。

而8刚好是在(5,10)这个间歇锁的范围内,所以session B的插入语句会被阻塞,而session C的插入语句不会被阻塞,10不在(5,10)的区间内。

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

这个例子是关于覆盖索引上的锁

首先session A要给索引c上c=5这一行加上读锁。

  1. 根据原则1,加锁单位是next-key lock,因此会给(0,5]加上next-key lock。
  2. 因为c是普通索引,不保证唯一性,所以只找到c=5的时候还不可以停下来,根据原则二,访问到的都得加锁,那么10被访问到了,10也得加锁,所以区间为(5,10]。
  3. 根据优化2,等值判断,向右一直遍历到不满足的时候位置,然后next-key lock退化为间歇锁,因此,锁的区间变为(5,10)。
  4. 这个查询用到了覆盖索引,所以不需要回表,那么主键索引树上也就不会加锁了

为什么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);
  1. 访问路径

    • 插入新记录 (7,7,7) 会尝试在索引 c 中找到插入位置。
    • 索引 c(5,10) 范围已经被 session A 加了 Gap Lock
  2. 锁冲突

    • Gap Lock 阻止了任何事务在 (5,10) 范围内插入新记录。
    • 因此,session C 被阻塞。
分类: MySQL 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录