一、事务的幻读问题
串行化隔离级别下如何解决事务并发存在的幻读问题?在MySQL事务章节中有提到幻读(虚读)。
虚读:一个事务的操作导致另一个事务前后两次查询的结果,数据量不同。例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增加的数据或者读不到事务A删除的数据)
这里的过滤条件包括等值查询、范围查询;间隙锁(gap lock)、 record lock(行锁)
二、主键索引(值不可以重复的)的间隙锁
在事务使用范围条件作为过滤,而不是相等条件检索数据,并请求共享或排他锁时,InnoDB存储引擎会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)” ,间隙的范围和过滤条件一样的,InnoDB存储引擎也会对这个范围加锁,这种锁机制就是所谓的间隙锁。
举例来说, 假如 stu 表中只有 5 条记录, 其 uid 的值分别是 1,2,3,4,5,6, 下面的 SQL:
select * from stu where uid > 5 for update;
这个SQL是一个范围条件的检索,InnoDB存储引擎不仅会对符合条件的 uid 值为6的记录行加锁,也会对uid 大于 6(即使这些记录并不存在)的"间隙"加锁,防止其它事务在表的末尾增加数据。
InnoDB存储引擎使用间隙锁的目的,为了防止幻读,以满足串行化隔离级别的要求。对于上面的例子,在不使用间隙锁的情况下,如果其它事务插入了 uid 大于 5 的任何记录,那么本事务如果再次执行上述语句,查询结果的数据量发生变化,就说明发生幻读。
串行化隔离级别下如何解决事务并发存在的幻读问题?通过间隙锁解决的。
三、二级索引(值可以重复的)的间隙锁
1、范围查询时
利用二级索引进行范围查询时,由于非主键索引的属性值可以重复,所以间隙锁的范围会变成大于等于这个值的左闭区间。通过例子来理解:age添加了辅助索引,查询年纪大于20的记录行
select *from stu where age > 20;
二级索引的值相等的话,主键按照升序排列,如上图中所示,该范围处在间隙锁的范围之中,所以会发生事务阻塞。
2、等值查询时
在串行化隔离级别下,对辅助索引做等值查询时,为了防止幻读,InnoDB存储引擎会对这个值所在的记录行添加行锁,并在记录行的两边添加间隙锁。例如下图:实际的索引树不会数据量不会这么小,这里只是为了方便画图。
select *from stu where age > 19;
在这个例子中,间隙的范围在age为[15,19]和[19,23)的这么一个范围。
总结
串行化隔离级别下InnoDB存储引擎通过间隙锁来解决事务并发产生的幻读问题;在主键索引、唯一键索引,值不允许被重复的情况下与值可以重复的二级索引,它们的间隙范围是有所不同的。