mysql行锁划分
记录锁
索引只能是主键或者唯一索引的等值条件查询。,记录锁就是为某行记录加锁,
如果主键或者唯一索引的等值查询的条件不存在,则将变为临键锁
。
CREATE TABLE `t_a` (
`id` int NOT NULL AUTO_INCREMENT,
`age` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 原始数据
select * from t_a;
+----+-----+
| id | age |
+----+-----+
| 1 | 1 |
| 5 | 5 |
| 7 | 7 |
+----+-----+
实践
加锁主键索引有查询记录存在
- 实操
-- [session【1】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 加互斥锁成功
select * from t_a where id = 5 for update;
-- [session 【2】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 等待 session-1的释放
select * from t_a where id = 5 for update;
-- 可以对 主键索引id=6 加锁
select * from t_a where id = 6 for update;
- 结论
当查询条件的主键索引或者唯一索引 有查询记录存,则会 锁住当前唯一行的记录,
加锁主键索引查询记录不存在
- 实践
-- [session【1】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 加互斥锁成功
select * from t_a where id = 2 for update;
-- [session 【2】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 插入一条 id=3的记录,发现不能插入,此时会等待 session1的 互斥锁释放。
insert into t_a(id,age) values(3,3)
-- 查如 id=6则可以插入成功
insert into t_a values(6,6);
- 描述
对查询主键id=2的记录增加互斥锁,当查询记录不存在 记录id有[1,5,7],则记录锁就会变为 临键锁
,锁住(1,2]和(2,5]的之间的记录。
在session2中 当你插入 记录id=6的时候,是可以插入的,因为他不在锁的范围
- 结论
当对加锁的主键索引/唯一索引检索时,记录锁会 变为 临键锁。
间隙锁(gap-lock)
参考链接
间隙锁,锁定一个范围,间隙锁是封锁索引记录的范围,或者第一条之前的记录,又或者是最后一条之后的记录,置于范围是开区间还是闭区间,取决于当时 使用索引范围的查询
条件,
比如:记录[1,3,5,7]
id>=1 and id <=5则 锁的为范围 [1,5]
id >3 则 (3,+00)
id < 3 则 (-00,3)
id >4 and id < 7 则 使用临近的索引3(3,7)
产生间隙锁的条件:
0、事务隔离界别RR,
0、使用索引做范围的查询,
0、使用二级索引做等值查询,(因为不唯一性,可能索引明智多条记录,符合范围)
实践
加锁索引的范围查询
CREATE TABLE `t_a` (
`id` int NOT NULL AUTO_INCREMENT,
`age` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 原始数据
select * from t_a;
+----+-----+
| id | age |
+----+-----+
| 1 | 1 |
| 3 | 3 |
| 5 | 5 |
| 7 | 7 |
+----+-----+
- case1-查询的范围存在(使用 唯一索引做 范围查询)
-- [session【1】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 加互斥锁成功
select * from t_a where id between 1 and 5 for update;
-- [session 【2】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 等待 session-1的释放,
insert into t_a values(4,4)
-- 则也会等待 session1的释放。
select * from t_a where id = 1 for update;
- Case1-描述
session1索引锁定记录范围[1,5]因为是 between and ,
session2 则不能在该范围内插入 符合范围的 4自增主键id,
Sesson2 也不能对 1 增加互斥锁。
- case2-查询的范围不存在
-- [session【1】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 加互斥锁成功
select * from t_a where id >10 for update;
-- [session 【2】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 等待 session-1的释放,
insert into t_a values(11,11)
-- 等待 session-1的释放,
insert into t_a values(8,8)
- Case2-描述
数据记录只有【1,3,5,7】
session1的 索引查询范围是 id>10, 但是索引里面没有10,所以临近原则,就用 (7,+00)表示了,
所以在操作 session2 添加,11的时候添加不进去,同时在添加 8的时候也添加不上去,所以就能说明的是 加锁范围是 (7,+00)
- 结论
使用索引在做条件范围查询时,锁住的区间范围取决于,
1、索引条件查询不到数据,则根据临近原则,选择合适的 索引值来做为范围(比如 id>100,则用 id>7作为范围)
2、索引条件查到数据了,但是索引条件值不在索引中则,也是使用临近原则,(比如,id>4 and id <6 则用 id >3 and i<7来替换)
3、索引条件查到数据,索引条件值也在索引中,则根据的条件范围进行区间锁定。
临建锁(next-key lock)
临键锁,是记录锁与间隙锁的组合,它的封锁范围,上下锁定区间范围。是左闭右开。存在于非唯一索引中,
临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效
实践
CREATE TABLE `t_a` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(16) NOT NULL DEFAULT '',
`age` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
mysql> select * from t_a;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | a | 10 |
| 2 | b | 24 |
| 3 | c | 32 |
| 4 | d | 45 |
+----+------+-----+
-- [session【1】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 加互斥锁成功
select * from t_a where age=24 for update;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 2 | b | 24 |
+----+------+-----+
-- [session 【2】]
SET autocommit=0;
SET session transaction isolation level REPEATABLE READ;
-- 插入25,则需要等待session1,释放锁 (24,32]
insert into t_a ('name','age') values('d',25);
-- 插入23,则需要等待session1,释放锁,(10,24]
insert into t_a (name,age) values('d',23);
- 描述
从查询中的数据中,临键锁的划分范围是
(negative infinity, 10]
(10, 24]
(24, 32]
(32, 45]
(45, positive infinity)
session1的 执行 age=24,(非唯一索引的等值查询),这个时候是 ”记录锁”+ "间隙锁" (锁定了 (10,24] 和 (24,32] 上下区间),
session在做,insert age=25, 和 age=23,刚好在 session的 临键锁的区间内,则无法进行操作。
- 结论
使用二级索引做等值查询,或者 使用唯一索引做等值查询不存在时, 会加上临键锁的。
如果等值查询的值在 索引区间范围内(出过 正无穷 和 负无穷)则会 锁住改值的 上线两个区间的,