MySQL InnoDB锁的算法

71 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

概念

t2 这张表有一个主键索引。

image.png

这些数据库里面存在的主键值,我们把它叫做 Record,记录,那么这里我们就有 4 个 Record。

根据主键,这些存在的 Record 隔开的数据不存在的区间,我们把它叫做 Gap,间隙,它是一个左开右开的区间,如果还有同学分不清开区间和闭区间的区别。

间隙(Gap)连同它右边边的记录(Record),我们把它叫做临键的区间,它是一个左开右闭的区间。

记录锁

当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。 比如 where id = 1 4 7 10 。

间隙锁

当我们查询的记录不存在,没有命中任何一个 record,无论是用等值查询还是范围查询的时候,它使用的都是间隙锁。

举个例子,where id >4 and id <7,where id = 6。

Transaction 1Transaction 2
begin;
INSERT INTO t2 (id, name) VALUES (5, '5'); // BLOCKED
INSERT INTO t2 (id, name) VALUES (6, '6'); // BLOCKED
select * from t2 where id =6 for update; // OK
select * from t2 where id >20 for update;
INSERT INTO t2 (id, name) VALUES (11, '11'); // BLOCKED

注意,间隙锁主要是阻塞插入 insert。相同的间隙锁之间不冲突。

Gap Lock 只在 RR存在,如果要关闭间隙锁,就是把事务隔离级别设置成 RC, 并且把 innodb_locks_unsafe_for_binlog 设置为 ON

这种情况下除了外键约束和唯一性检查会加间隙锁,其他情况都不会用间隙锁。

临界锁

当我们使用了范围查询,不仅仅命中Record 记录,还包含Gap 间隙,在这种情况下我们使用的就是临键锁,它是 MySQL 里面默认的行锁算法,相当于记录锁加上间隙锁。

比如我们使用>5 <9, 它包含了记录不存在的区间,也包含了一个 Record 7。

Transaction 1Transaction 2
begin;
select * from t2 where id >5 and id < 9 for update;
begin;
select * from t2 where id =4 for update; // OK
INSERT INTO t2 (id, name) VALUES (6, '6'); // BLOCKED
INSERT INTO t2 (id, name) VALUES (8, '8'); // BLOCKED
select * from t2 where id =10 for update; // BLOCKED

临键锁,锁住最后一个 key 的下一个左开右闭的区间。

select * from t2 where id >5 and id <=7 for update; -- 锁住(4,7]和(7,10] 
select * from t2 where id >8 and id <=10 for update; -- 锁住 (7,10],(10,+∞)

为什么要锁住下一个左开右闭的区间?——就是为了解决幻读的问题。