Record lock,Gap Lock ,next-key Lock三种锁机制:
首先,还是假设我们有表t:
| sn_id (primary key) | ID(unique 索引) | age (普通索引) | num(非索引) |
|---|---|---|---|
| 5 | 50 | 24 | 123 |
| 10 | 100 | 30 | 456 |
| 15 | 150 | 24 | 789 |
下面举的所有例子都是以这个表t为基础。
好了,现在让我们来介绍这个三种锁:
其实这三种锁的形式,用这张图就大概可以表明了:

record Lock:又称为记录锁,是针对索引记录(index record)的锁定,是简单的一种锁形式。一般都是命中索引后,对该索引加锁。如:
select * from t where sn_id=5 for update;
select * from t where age=24 and num=123 for update;(这个在某些场景下会升级成gap lock)
就是记录锁。
Gap Lock:锁定的是索引记录之间的间隙、第一个索引之前的间隙或者最后一个索引之后的间隙,如上面表记录存在的间隙就有(-∞,5),(5,10),(10,15),(15,+∞)。
这里有两点需要注意下
1.间隙表只有在可重复读的隔离级别或者更高的级别才会有,并且参数innodb_locks_unsafe_for_binlog为ON
2.间隙表主要的目的是解决幻读。
Next-key Lock:既会锁表记录的间隙,也会锁住表记录,相当于上面record Lock+Gap Lock。
1.检索条件为主键:
1)命中主键
SELECT * FROM t WHERE sn_id =5 FOR UPDATE;这时是record锁,锁住了sn_id=5,这条记录。因为命中了唯一的条件,不会有幻读的情况发生。
2)命中某个范围
例如:事务A执行了下面的sql:
SELECT * FROM t WHERE sn_id BETWEEN 5 and 10 FOR UPDATE;那事务B想要执行这个sql,就会报错:
insert into t values (8,8,8);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
因为在执行事务A时,已经将(5,10】这整个区间都锁住了,因此想要向这个区间插入数据,会报等锁时间超长的错。(PS:这里的5到10是一个左闭右开区间,因为在RR的隔离条件下,范围匹配时,会锁住遍历范围的最后一条索引记录,即sn_id=10)
3)未命中
如事务A执行了下面的sql:
SELECT * FROM t WHERE sn_id=6 FOR UPDATE;这时由于没有命中索引,其锁住的范围为sn_id=6左右范围的索引,即(5,10)这个区间,这样就会防止有新的sn_id=6插入,从而造成幻读。
2.检索条件为唯一索引:
1)命中索引
SELECT * FROM t WHERE ID =50 FOR UPDATE;这时是不存在gap锁的,而只是record锁,因为命中了唯一的条件,不会有幻读的情况发生。
2)命中某个范围
SELECT * FROM t WHERE ID >50 and ID<100 FOR UPDATE;这里在表中记录遍历的范围为ID∈(50,100)
针对二级索引ID(50,100】这个范围内都会被lock起来,(范围匹配时,会锁住遍历范围的最后一条索引记录,所以ID=100会被锁住)。
针对聚簇索引,则不会进行加锁。
如果语句是
SELECT * FROM t WHERE ID >=50 and ID<100 FOR UPDATE;二级索引ID【50,100】这个范围内都会被lock起来,同时聚簇索引sn_id= 5 也会被锁住。
3)未命中
SELECT * FROM t WHERE ID =60 FOR UPDATE;这里在表中记录遍历的范围为 ID∈(50,100)
二级索引ID (50,100)这个范围内都会锁起来,由于这个是精确匹配,不会在最后的遍历值上加锁。聚簇索引上也不会有锁。
3.非唯一索引:
1)命中索引
假设事务A执行下面的sql
SELECT * FROM t WHERE age =24 FOR UPDATE;这时候由于是非唯一的索引,为防止出现幻读的情况,不光会在age=24这个索引上加锁,还会在(-∞,24)(24,30)这个间隙中加锁。同时,age =24的索引ID和sn_id都会加上锁。
2)命中某个范围
SELECT * FROM t WHERE age >20 and age< 25 FOR UPDATE;这里在表中记录遍历的范围为 age∈(-∞,24】(24,30)
那锁住的范围其实就是(-∞,24】,(24,30],由于是范围检索,会锁住遍历范围的最后一条索引记录。
3)未命中
SELECT * FROM t WHERE age =25 FOR UPDATE;这时锁住的范围其实就是 age∈(24,30),
如果没有where条件没有命中任何索引,则全表会被锁。
4.最后的小结:
1)唯一索引精确等值检索,Next-Key Lock就退化为record lock,不会加gap lock
范围检索,会锁住where条件中相应的范围,范围中的记录以及间隙,换言之就是加上record lock和gap lock。
2)非唯一索引精确等值检索,Next-Key Lock 会对间隙加gap锁,以及对应检索到的record lock。
范围检索,会锁住where条件中相应的范围,范围中的记录以及间隙,换言之就是加上record lock和gap lock。
3)非索引检索,全表间隙加gap lock,全表记录加record lock




