一、介绍
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别 运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。
- 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。
- 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。
- 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。
注意:间隙锁唯一目的是防止其他事务插入间隙,造成幻读现象。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
对于间隙锁和临键锁大家不用去记,主要针对于某一条SQL,你能够分析出它为什么要加这个锁就行了。
二、演示
A. 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。
id为5的记录并没有,当我们执行这条update语句,并不是将id为5的这一行记录锁住,此时它会对3和8之间的间隙加一个间隙锁。
lock_data
为 8,代表的是它所的是8之前的这块间隙,简单说就是3 ~ 8的这块间隙,注意间隙锁不包含当前的这个记录,3和8不包含,锁的只是3和8之间的这一块的间隙。
既然锁的是3和8之间的间隙,只有将左边的事务提交之后,此时间隙锁就会释放掉,释放掉之后,右边就可以执行插入了。
B. 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。
介绍分析一下:
当我们进行等值查询时,如果不是唯一索引,我们在加行锁的时候,它是针对于索引加的锁,我们知道InnoDB索引是一个B+树结构,叶子节点是有序的双向链表。 假如,我们要根据这个二级索引查询值为18的数据,并加上共享锁,我们是只锁定18这一行就可以了吗? 并不是,因为是非唯一索引,这个结构中可能有多个18的存在,所以,在加锁时会继续往后找,找到一个不满足条件的值(当前案例中也 就是29)。此时会对18加临键锁,并对29之前的间隙加锁,但并不会对29加锁。
左边如果仅仅执行select语句,它不会加任何锁,我们要想看到加锁的情况,在后面可以加上 lock in share mode
,这样就代表加了一把共享锁。执行成功后,我们就需要到右边来看一下此时InnoDB引擎在加锁的时候它是怎么去加的,是只对id为3的这一行数据加了一个锁吗?实际上并不是的。
我们要想去看一下InnoDB引擎是怎么去加锁的,还是只需要去执行一下刚才的SQL语句,看一下加锁的情况就行了。
我们主要来看一下这些行级锁,上面表级的意向锁我们不用去看。
首先我们可以看到,加了一个S,代表的是共享锁。3, 3
指的是id为3的这行记录,首先它会对现有的记录加一个行锁,第三条记录加的就是行锁,REC_NOT_GAP
没有间隙;S
代表临键锁
C. 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。
supremum pseudo-record
可以理解为正无穷。
查询的条件为id>=19,并添加共享锁。 此时我们可以根据数据库表中现有的数据,将数据分为三个部 分:
[19]
(19,25]
(25,+∞]
所以数据库数据在加锁是,就是将19加了行锁,25的临键锁(包含25及25之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。