Mysql记录锁、间隙锁、临键锁

240 阅读2分钟

概念

记录锁:Record Locks 即行锁

间隙锁:Gap Locks 锁住一个区间,左开右开

临键锁:Next-key Locks 锁住一个区间,左开右闭

在RR级别Mysql默认加锁方式即为临键锁,下面介绍mysql在RR级别下,各种情况加锁分析:

mysql加锁情况

创建一个表:c_test

id为主键,age列存在普通索引,balance列不存在索引。

image.png

以下测试均在RR级别,首先说明mysql的查询操作并不会加任何锁,因此不会存在锁冲突,我们以下案例均由查询语句后加for update给它加写锁

查询主键索引(唯一索引)

查询主键且能查到记录

查询同一记录

session1

image.png

session2

image.png

结果

image.png

可以发现查询同一记录会被锁住。而session2去查询其他任何记录都不会被锁住。

总结: 查询主键且能查到记录时,临键锁会降级为记录锁

查询主键且能查不到记录

session1

image.png

session2

image.png

执行结果

image.png

进行测试,发现在(2,5)的这个区间插入数据会被锁住,但是可以修改id为5的这条记录。 ps:为什么session2用插入来测试而不是select加for update,因为间隙锁针对的就是插入,而且在此区间不存在数据没法通过for update加锁。

总结:查询主键且查不到记录时,临键锁会降级为间隙锁

主键索引范围查找

比如执行下面sqlselect * from c_test where id>=2 and id<3 for update; 这条记录会锁住id为[2,5)的范围。

而执行这条sqlselect * from c_test where id>=3 and id<4 for update; 。则会锁住(2,5)的范围。

查询普通索引

1.当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。

比如执行此sql: SELECT * from c_test where age = 23 for UPDATE 不仅会锁(18,23]还会锁(23,30)

2.当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

而执行此sql:SELECT * from c_test where age = 20 for UPDATE,只会锁住(18,23)这个间隙。

3.当范围查找时

比如执行此sqlSELECT * from c_test where age >= 20 and age <22 for UPDATE 会锁住(18,23)这个间隙。

比如执行此sqlSELECT * from c_test where age >= 18 and age <22 for UPDATE 会锁住(负无穷,18)到[18,23)这个间隙。

查询不带索引记录

因为没有该索引去加锁,所以会将其全表锁住,比如执行下面的sqlselect * from c_test where balance = 100 for update; ,那么全表都不能进行插入修改等操作

总结

锁是加在索引上的。若不好记忆可以思考,如何加锁可以解决幻读和不可重复读的问题,mysql底层就是如此考虑的。