1. 在同一个事务中,两次读取的数据不一致的情况称为幻读和不可重复读。
      1. 幻读是针对insert导致的数据不一致。
      2. 不可重复读是针对delete、update导致的数据不一致 。
    2. 幻读-一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
    3. 在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在’当前读’下才会出现。
    4. 如何解决幻读
      1. 间隙锁(Gap lock):锁住两个值之间的空隙。<开区间’()’>
      2. 产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的’间隙’,因此,innodb引入间隙所可以解决幻读问题。
      3. insert into t values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);
        1. image.png
        2. 插入6个记录,会产生7个间隙。
        3. 当执行 select * from t where d = 5 for update;
        4. 不止给数据库已有的6个记录加上了行锁,还同事加了7个间隙锁,这样确保无法插入新的记录。
        5. 在一行行扫描的过程中,不仅将给行加上了行锁,还给行两边的空隙,也加上了间隙锁。
      4. 数据行是可以加上锁的实体,数据行之间的间隙,也是可以加上锁的实体。
    5. 行锁-分成为读锁和写锁
      1. image.png
      2. 跟行锁有冲突的是另一个行锁。
    6. 跟间隙锁存在冲突关系的,是往这个间隙中插入一个记录。间隙锁之间都不存在冲突关系。
      1. image.png
      2. sessionB不会被堵住,因为表t里并没有c=7这个记录,因此sessionA加的是间隙锁(5,10)。而sessionB也是在这个间隙加的间隙锁。他们共同的目标,即:保护这个间隙,不允许插入值,但他们之间是不冲突的。
    7. next-key lock:间隙锁和行锁的合称<(]’前开后闭合区间’>。
      1. 每个next-key lock都是前开后闭区间。
      2. 表t初始化以后,如果用select * from t for update会把这个表所有的记录锁起来,形成7个next-key lock分别是(-∞,0],(0,5],(5,10],(10,15],(15,20],(20,25],(25,+supremum]。
      3. 因为+∞是开区间,实现上,innodb给每个索引加了一个不存在的最大值supremum,才符合’next-key lock都是前开后闭区间’。
    8. image.png
      1. 假设N=9。
      2. sessionA执行 select … for update语句,由于id=9这一行并不存在,因此会加上间隙锁(5,10)。
      3. sessionB执行select … for update语句,同样会加上间隙锁(5,10),间隙锁之间不会冲突,因此这个语句执行成功。
      4. session B 试图插入一行(9,9,9),被sessionA的间隙锁挡住了,只好进入等待。
      5. sessionA试图插入一行(9,9,9),被sessionB的间隙锁挡住了。
      6. 因此,两个session进入互相等待状态,形成死锁。innnodb的死锁检测马上就会发现这对死锁关系,让sessionA的insert语句报错返回。
    9. 间隙锁是在可重复读隔离级别下才会生效的。如果把隔离级别设置为读提交的话,就没有间隙锁了,但同时,可能会出现的数据和日志不一致的问题,需要把binlog格式设置为row。