MySQL的innoDB引擎虽然拥有标准的四级隔离级别,不过它有其他数据库不同,就是在可重复读 RR 级别下面已经可以防止幻读的发生。,所谓幻读,指的是事务A执行过程中,由于事务B并发插入或删除了多条新数据,事务A两次读数据的行数不一样,出现了“虚幻”的新纪录(phantom,幽灵)。
在快照读(snapshot read)的情况下,MySQL通过MVCC(多版本并发控制)来避免幻读。
原理:将历史数据存一份快照,所以其他事务增加与删除数据,对于当前事务来说是不可见的。
快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。主要应用于无需加锁的普通查询(select)操作。
快照读sql语句如下
select * from table where ?;
在当前读(current read)的情况下,当前读,读取的是记录的最新版本,并且会对当前记录加锁,防止其他事务发修改这条记录。MySQL通过next-key lock来避免幻读。
当前读sql语句如下:
select * from table where ? lock in share mode; select * from table where ? for update; insert; update; delete;
InnoDB有三种行锁的算法:
1、Record Lock 行锁:单个行记录(索引记录)上的锁。
1、Gap Lock 间隙所:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
1、Next-Key Lock :1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
因为InnoDB对于当前读 行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围(GAP)。
假设索引a有1,3,5,8,11,其记录的GAP的区间如下:是一个左开右闭的空间(原因是默认主键的有序自增的特性,结合后面的例子说明)
(-∞,1],(1,3],(3,5],(5,8],(8,11],(11,+∞)
select * from t where a = 3 for update;
那么锁住的范围有 (1,3)3,(3,5] 即 当前行+两边的间隙 (1,3],(3,5],
这些范围的行数据和索引都被锁住,所以可以防止insert或者delete 带来的幻读
但是注意,对于可重复读默认使用的就是next key lock,但是对于“唯一索引” ,比如主键的索引,next key lock会降级成行锁Record Lock ,即仅锁住索引本身,而不会锁住一个区间。