1、行锁是什么?
字面意思,给一行数据加锁。
InnoDB 行锁是通过给索引上的索引项加锁来实现的,如果没有索引,InnoDB 将通过隐藏的聚簇索引来对记录加锁。
注意:行锁必须有索引才能实现,否则会自动锁全表,那么就不是行锁了。 两个事务不能锁同一个索引。
2、三种算法
(1)Record lock:记录锁,单个行记录上的锁
锁住具体的索引项,当SQL执行按照唯一性索引进行数据的检索时,查询条件等值匹配且查询的数据是存在,这时SQL语句加上的锁即为记录锁Record locks,锁住具体的索引项
BEGIN;-- 开始事务
SELECT * FROM my_table WHERE id = 2021 FOR UPDATE; -- 排它锁的记录锁算法
ROLLBACk;-- 回滚事务
# 普通事务执行
SELECT * FROM my_table WHERE id = 2021 FOR UPDATE;
SELECT * FROM my_table WHERE id = 2020 FOR UPDATE;
(2)Gap lock:间隙锁,锁定一个范围,但不包含记录本身
- 当sql执行按照索引进行数据的检索时,查询条件的数据不存在,这时SQL语句加上的锁即为 Gap locks,锁住索引不存在的区间(左开右开的区间)
- Gap只在RR事务隔离级别/repeatable read存在
BEGIN;
SELECT * FROM my_table WHERE id > 2001 AND id < 2021 FOR UPDATE; --排它锁的间隙锁算法
SELECT * FROM my_table WHERE id = 2021 FOR UPDATE; -- 另一种方式
ROLLBACK;
(3)Next-key lock:record+gap,临建锁,锁定一个范围,且包含记录本身
- 锁住记录+区间(左开右闭的区间)
- 当sql执行按照索引进行数据的检索时,查询条件为范围查找(between and、<、>等)并有数据命中则此时SQL语句加上的锁为Next-key locks,锁住索引的记录+区间(左开右闭的区间)
BEGIN;
SELECT * FROM my_table WHERE id > 2001 AND id < 2021 FOR UPDATE; --临建锁的间隙锁算法
-- 其他事务执行
set session autocommit=off;
select * from t2 where id=4 for update; --没锁住
select * from t2 where id=7 for update; --锁住
select * from t2 where id=10 for update; --锁住
INSERT INTO `t2` (`id`, `name`) VALUES (9, '9');
ROLLBACK;