结论(mysql版本8.0.29)
操作的字段建有唯一索引:
- 记录锁对应记录存在时,为行锁;不存在时,为间隙锁;
- 加锁范围两端值均存在记录时,仅锁住范围内间隙;某端值不存在或者都不存在时,取端值所在间隙加锁;
操作的字段仅有非唯一索引:
- 记录锁对应记录存在时,为临键锁;不存在时,为间隙锁;
- 加锁范围两端值存在与否都取其所在间隙加锁;
注: mysql 版本的不同,在以上的处理上有所差异;但是相同点在于均区分是否建有唯一索引
记录锁(Record Locks)
A record lock is a lock on an index record.
作用:阻止其他事务插入/删除/更新该记录
使用示例:
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
/* 此处假定c1列建了唯一索引,故仅锁住该行 ,注意与下面的Next-key Locks区分 */
注:
- 记录锁仅作用在索引上,如果没有索引,InnoDB 会创建一个隐藏的聚集索引,然后使用记录锁
- 如果加锁的记录存在,则对该记录加锁;如果该记录不存在,变成间隙锁;(mysql 版本8.0.29)
间隙锁(Gap Locks)
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record
作用:阻止其他事务插入该间隙的记录(即使该记录不存在)
使用示例:
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
注:
- 一般情况下,使用唯一索引的查询条件没有间隙锁,如果c1建有唯一索引?仅锁住区间内的记录(包含不存在的),如果是间隙锁,则与前部分声明相悖,如果是记录锁,那么幻读问题得不到解决,所以此处更像是一种区间锁(此处实际上的处理看mysql版本);特殊情况,唯一索引包含多列,且查询条件仅包含其中的某些列,也会产生间隙锁
- 如果c1 建有唯一索引,且查询范围两端值均存在记录,则只锁住范围内的间隙,即[10,20] (mysql版本8.0.29,旧版本统一处理成间隙锁); 如果某端值不存在,则对其所在间隙加锁;
- 如果设置隔离级别为读已提交(默认是可重复读),扫描索引时不会加间隙锁,完整性检查时才加锁
临键锁(Next-Key Locks)
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record
作用:记录锁与间隙锁的作用集合
使用实例:
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
/* 此处假定c1列没有唯一索引,此处锁住该行以及之前的间隙,如果该记录不存在-->间隙锁;注意与上面的Record Locks区分 */
注:默认情况下,InnoDB 事务隔离级别(版本8.0.29)为可重复读(REPEATABLE READ),扫描索引时使用临键锁,解决幻读问题(phantom rows)