最近群里不少人讨论了一些高质量的问题。我今天抽时间给大家总结一个 MySQL InnoDB 存储引擎各种不同 SQL 情况下,加行锁、间隙锁、next-key lock 做一个总结。如果有错误的地方,请大家指正!
为了讲清楚相关加锁的情况,我们先来创建一个测试验证用的表。结构如下所示:
CREATE TABLE `xttblog` (
`id` bigint(20) NOT NULL,
`name` varchar(16) COLLATE utf8mb4_bin DEFAULT NULL,
`idcard` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
然后插入几条测试数据。
insert into `xttblog` values(1,’’,100),(5,'xttblog’,200),
(8,’xttblog’,300),(10,’’,400);
在开始之前,我先来解释一下,RR 和 RC。
RR 隔离级别就是,可重复读。REPEATABLE READ。
RC 隔离级别就是,读已提交。READ COMMITTED。
搞明白 RR 和 RC 后,我们先来看第一种情况。查询条件是聚簇索引,也就是主键索引的精确匹配情况。
// 在id=1的聚簇索引上加X锁
update `xttblog` set name=‘x' where id=1;
// 在id=1的聚簇索引上加S锁
select * from `xttblog` where id=1 lock in share mode;
上面这种情况下,如果 SQL 是精确查询,不管是 RR 还是 RC 隔离级别下,都会在命中的索引上加 record lock(行锁)。X 锁是排它锁(Exclusive Lock),也有人称作是写锁。S 锁是共享锁(Shared Lock),也有人称作是读锁。不管是 X 锁,还是 S 锁,都是加在行上的。注意,行锁不是加在记录上的,而是加在索引上的!
第二种情况,如果是范围查询,那么在在 RC 隔离级别下,会在所有命中的行的聚簇索引上加 record locks(锁行)。因为 RC 级别下是没有间隙锁的。
// 在id=8和10的聚簇索引上加X锁
update `xttblog` set name='极客时间返现24' where id>7;
// 在id=1的聚簇索引上加X锁
update `xttblog` set name='极客时间返现24' where id<=1;
第三种情况,在 RR 隔离级别下,会在所有命中的行的聚簇索引上加 next-key locks(锁住行和间隙)。最后命中的索引的后一条记录,也会被加上 next-key lock。
// 在id=8、10(、+∞)的聚簇索引上加X锁
// 在(5,8)(8,10)(10,+∞)加gap lock
update `xttblog` set name=‘x' where id>7;
// 在id=1、5的聚簇索引上加X锁
// 在(-∞,1)(1,5)加gap lock
update `xttblog` set name=‘x' where id<=1;
第四种情况,如果查询结果为空,即没有命中任何聚族索引,那么,在 RC 隔离级别下,什么也不会锁。
第五种情况,如果查询结果为空,即没有命中任何聚族索引,那么,在 RR 隔离级别下,会锁住查询目标所在的间隙。
// 在(1,5)加gap lock
update `xttblog` set name=‘x' where id=2;
以上,对于主键索引加锁的场景基本上已经全部覆盖到了。如果你还有什么疑问想讨论的,请加我微信:xttblog。
RR 隔离级别实现起来,要比 RC 复杂的多。给你留一个问题,MySQL 是出于什么样的考虑,默认是采用 RR 隔离级别?
: » MySQL 主键索引在 RR 和 RC 隔离级别下的加锁情况总结
原创文章,作者:bd101bd101,如若转载,请注明出处:https://blog.ytso.com/tech/database/252430.html