初始化语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | mysql> CREATE TABLE `t` (     ->   `id` int(11) NOT NULL,     ->   `c` int(11) DEFAULT NULL,     ->   `d` int(11) DEFAULT NULL,     ->   PRIMARY KEY (`id`),     ->   KEY `c` (`c`)     -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.06 sec)
  mysql>  mysql> insert into t values(0,0,0),(5,5,5),     -> (10,10,10),(15,15,15),(20,20,20),(25,25,25); Query OK, 6 rows affected (0.04 sec) Records: 6  Duplicates: 0  Warnings: 0
   | 
 
隔离级别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | mysql> select @@tx_isolation; + | @@tx_isolation  | + | REPEATABLE-READ | + 1 row in set (0.01 sec)
  mysql> select @@global.tx_isolation; + | @@global.tx_isolation | + | REPEATABLE-READ       | + 1 row in set (0.00 sec)
   | 
 
幻读是什么?
 | 
sessionA | 
sessionB | 
seesionC | 
| T1 | 
begin; | 
 | 
 | 
| select * from t where d=5 for update;/*Q1/ result:(5,5,5) | 
 | 
 | 
 | 
| T2 | 
 | 
update t set d=5 where id=0; | 
 | 
| T3 | 
select * from t where d=5 for update;/*Q2/ result:(0,0,5),(5,5,5) | 
 | 
insert into t values(1,1,5); | 
| T4 | 
 | 
 | 
 | 
| T5 | 
select * from t where d=5 for update;/*Q3/ result:(0,0,5),(1,1,5),(5,5,5) | 
 | 
 | 
| T6 | 
commit; | 
 | 
 | 
1 2 3
   | 1.Q1 只返回 id=5 这一行; 2.在 T2 时刻,session B 把 id=0 这一行的 d 值改成了 5,因此 T3 时刻 Q2 查出来的是 id=0 和 id=5 这两行; 3.在 T4 时刻,session C 又插入一行(1,1,5),因此 T5 时刻 Q3 查出来的是 id=0、id=1 和 id=5 的这三行。
   | 
 
其中,Q3 读到 id=1 这一行的现象,被称为“幻读”。也就是说,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
1 2
   | 1.在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。 2.上面 session B 的修改结果,被 session A 之后的 select 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。
   | 
 
幻读有什么问题?
- 首先是语义上的。
 
- 其次,是数据一致性的问题。
 
- 也就是说,即使把所有的记录都加上锁,还是阻止不了新插入的记录
 
如何解决幻读?
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。间隙锁和 next-key lock 的引入,帮我们解决了幻读的问题,但同时也带来了一些“困扰”。间隙锁的引入,可能会导致同样的语句锁住更大的范围,这其实是影响了并发度的。