持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
概念
t2 这张表有一个主键索引。
这些数据库里面存在的主键值,我们把它叫做 Record,记录,那么这里我们就有 4 个 Record。
根据主键,这些存在的 Record 隔开的数据不存在的区间,我们把它叫做 Gap,间隙,它是一个左开右开的区间,如果还有同学分不清开区间和闭区间的区别。
间隙(Gap)连同它右边边的记录(Record),我们把它叫做临键的区间,它是一个左开右闭的区间。
记录锁
当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。 比如 where id = 1 4 7 10 。
间隙锁
当我们查询的记录不存在,没有命中任何一个 record,无论是用等值查询还是范围查询的时候,它使用的都是间隙锁。
举个例子,where id >4 and id <7,where id = 6。
| Transaction 1 | Transaction 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 1 | Transaction 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,+∞)
为什么要锁住下一个左开右闭的区间?——就是为了解决幻读的问题。