由于前文MySQL间隙锁写的不够细致,特补充此文对其进行补充
建表语句
基于MySQL8测试
create table test_lock(
id int not null auto_increment comment "主键",
uk_c int not null comment "唯一性索引",
index_c int not null comment "普通索引",
normal_c int not null comment "普通列",
primary key(id),
unique index uk_name(uk_c),
index index_name(index_c)
)engine = innodb;
INSERT INTO `test_lock` (`id`, `uk_c`, `index_c`, `normal_c`) VALUES (5, 5, 5, 5);
INSERT INTO `test_lock` (`id`, `uk_c`, `index_c`, `normal_c`) VALUES (10, 10, 10, 10);
INSERT INTO `test_lock` (`id`, `uk_c`, `index_c`, `normal_c`) VALUES (15, 15, 15, 15);
id | uk_c | index_c | normal_c |
---|---|---|---|
5 | 5 | 5 | 5 |
10 | 10 | 10 | 10 |
15 | 15 | 15 | 15 |
语句A:
begin;
-- 此时上下边界有两个,一个是index_c的5~15,另一个是主键id的5~15
select * from test_lock where index_c = 10 for update;
rollback;
语句B:
begin;
-- 可以成功插入
insert into test_lock(id,uk_c,index_c,normal_c) values(4,11,5,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(14,11,5,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(16,11,5,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(4,11,7,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(16,11,7,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(4,11,15,20);
-- 阻塞
insert into test_lock(id,uk_c,index_c,normal_c) values(14,11,15,20);
-- 成功插入
insert into test_lock(id,uk_c,index_c,normal_c) values(16,11,15,20);
rollback;
结论
对应的间隙范围有两个,一个是二级索引的间隙范围,还有一个是对应的间隙上下边界记录的主键的间隙范围。
- 当要插入的记录的二级索引值处于上下边界范围内时(不等于上下边界的值),阻塞。
- 当要插入的记录的二级索引值等于上下边界的值时,如果主键值在主键上下边界之内,阻塞。
- 当要插入的记录的二级索引值等于上下边界的值时,如果主键值在主键上下边界之外,成功插入。
- 当要插入的记录的二级索引值处于上下边界范围之外时,成功插入,不需考虑主键值。
所以当同时考虑主键的情况下,并没有网上各个分析间隙锁文章中说的左开有闭的说法。select … for update要慎用,一单查询的列没有索引的话,会锁住整张表,此时其他事务的写操作会阻塞。