初始化语句
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 的引入,帮我们解决了幻读的问题,但同时也带来了一些“困扰”。间隙锁的引入,可能会导致同样的语句锁住更大的范围,这其实是影响了并发度的。