行锁三种算法:
Record Lock:单个行记录上的锁
Gap Lock:间隙锁,锁定一个范围,但不会针对里面的每条记录上锁
Next-Key Lock:Gap Lock + Record Lock 锁定一个范围,且对记录本身也上锁
Record Lock:锁定的是索引,没有索引的话,锁定的是隐式的主键。
Next-Key Lock:此算法的锁定技术称为:Next-Key Locking
对于行的查询都是采用这种锁定算法,若一个索引有10,11,13,20四个值,则锁定的区间分别为:
(-∞,10] (10,11] (11,13] (13,20] (20,+ ∞)
还有previous-key locking 技术,它锁定的区间为:
(-∞,10) [10,11) [11,13) [13,20) [20,+ ∞)
若事务T1已经通过Next-Key Locking 锁定了如下范围:(10,11] (11,13]
当插入新的记录12时,则锁定的范围变为:(10,11] (11,12] (12,13]
当查询的索引含有唯一属性时,InnoDB会对Next-Key Lock进行降级处理,将其降级为Record Lock,即仅锁住索引本身
demo:
session1:
# 一:加入X锁
begin ;
select * from t_test_locking where id = 20 for update ;
# 三:查出id为18这条记录
select * from t_test_locking;
# 四:提交
commit ;
session2:
# 二:insert 加入的也是X锁(可以执行完,没有阻塞)
begin ;
insert into t_test_locking (id, name) values (19,190);
commit ;
分析:session1中的for update是加了X锁的,session2中insert 也是加X锁的。查询用的是Next-Key Lock算法,所以会锁定(18,20]这个区间,则session2中插入id=19应该是阻塞的,事实没有,所以发生了锁的降级。
当查询条件为辅助索引时(此时name字段添加了辅助索引):
select * from t_test_locking where name = 70 for update ;
此时其实添加了三把锁:
一:对于辅助索引,添加了传统的Next-Key Lock ,锁定的范围是(50,70)
二:因为有主键聚集索引,所以会在id为7的索引上添加Record Lock
三:InnoDB会对辅助索引的下一个键值添加Gap Lock,范围是(70,100)
Phantom Problem(幻像问题):
同一个事务下,连续执行俩次同样的SQL语句可能会导致不同的结果,第二次的SQL语句可能会返回之前不存在的行
默认隔离级别(REPEATABLE READ)下,InnoDB采用Next-Key Locking 机制避免幻读
在这篇里面:https://blog.csdn.net/qq_35970057/article/details/108365061
其实就是解释了第七步的原因,默认隔离级别REPEATABLE-READ时,用的是Next-Key Locking算法,锁的不仅仅是这一条记录,将后面的范围也锁住了,所以只有当第二个事务提交了,且第一个事务也提交了后,第一个事务才能看到第二个事务提交后的内容