概念
记录锁:Record Locks 即行锁
间隙锁:Gap Locks 锁住一个区间,左开右开
临键锁:Next-key Locks 锁住一个区间,左开右闭
在RR级别Mysql默认加锁方式即为临键锁,下面介绍mysql在RR级别下,各种情况加锁分析:
mysql加锁情况
创建一个表:c_test
id为主键,age列存在普通索引,balance列不存在索引。
以下测试均在RR级别,首先说明mysql的查询操作并不会加任何锁,因此不会存在锁冲突,我们以下案例均由查询语句后加for update给它加写锁
查询主键索引(唯一索引)
查询主键且能查到记录
查询同一记录
session1
session2
结果
可以发现查询同一记录会被锁住。而session2去查询其他任何记录都不会被锁住。
总结: 查询主键且能查到记录时,临键锁会降级为记录锁
查询主键且能查不到记录
session1
session2
执行结果
进行测试,发现在(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底层就是如此考虑的。