幻读:仅指当前插入的行

    可重复读隔离级是由 MVCC(多版本并发控制)实现的,实现的方式是启动事务后,在执行第一个查询语句后,会创建一个视图,然后后续的查询语句都用这个视图,「快照读」读的就是这个视图的数据,视图你可以理解为版本数据,这样就使得每次查询的数据都是一样的。

    快照读:普通的select是快照读 快照读是不会看到别的事务插入的数据
    当前读:除了快照读都是当前读 比如update delete insert

    这些语句执行前都会查询最新版本的数据,然后再做进一步的操作。
    假设你要 update 一个记录,另一个事务已经 delete 这条记录并且 提交事务了,这样不是会产生冲突吗,所以 update 的时候肯定要知道最新的数据。
    另外,select … for update 这种查询语句是当前读,每次执行的时候都是读取最新的数据。
    因此,要讨论「可重复读」隔离级别的幻读现象,是要建立在「当前读」的情况下。

    所以,Innodb 引擎为了解决「可重复读」隔离级别使用「当前读」而造成的幻读问题,就引出了 next-key 锁,就是记录锁和间隙锁的组合。
    记录锁,锁的是记录本身;
    间隙锁,锁的就是两个值之间的空隙,以防止其他事务在这个空隙间插入新的数据,从而避免幻读现象。
    比如,下面事务 A 查询语句会锁住
    (2, +∞]范围的记录,然后期间如果有其他事务在这个锁住的范围插入数据就会被阻塞。
    image.png
    需要注意的是,next-key lock锁的是索引,而不是数据本身,所以如果update语句的where条件没有用到索引列,那么就会全表扫描,在一行行扫描的过程中,不仅给行加上了行锁,还给行两边的空隙也加上了间隙锁,相当于锁住整个表,然后直到事务结束才会释放锁。

    所以在线上千万不要执行没有带索引条件的update语句,不然会造成业务停滞,-key锁的加锁规则其实挺复杂的,在一些场景下会退化成记录锁或间隙锁,对记录加锁时,加锁的基本单位是next-key lock,它是由记录锁和间隙锁组合而成的,next-keylock是前开后闭区间,而间隙锁是前开后开区间。
    但是,next-key lock在—些场景下会退化成记录锁或间隙锁。

    InnoDB有三种行锁的算法:
    1,Record Lock:单个行记录上的锁。
    2,Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
    3,Next-Key Lock: 1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。