先来简单的复习一下数据库隔离级别html
一个事务A读取到另外一个事务B未提交的修改,B回滚了,就形成了数据不一致。(现象:脏读)git
一个事务A在事务执行过程当中第一次读取的值和第二次读取的值不一致,这是因为事务B在俩次读取之间修改了数据并提交了事务。(现象:不可重复读)github
一个事务A在事务执行过程当中第一次读取的值和第二次读取的值一致(解决了不可重复读),可是其余事务B 的insert 或者 delete的操做,会影响到俩次查询的条数(现象:幻读)数据库
最高的事务隔离级别,串行化。bash
问题来了:RR下会出现幻读,那为何innodb能在RR下解决幻读呢?不是互相矛盾吗?并发
解释:这是因为innodb和标准不一致致使的。具体能够看看github火热的讨论Innodb RR 下可否防止幻读?ui
意思就是MVCC判断了记录的可见性,好比 select count(*) from table where col_name = xxx 时(属于快照读),在RR 级别下,这条事务在事务一开始就生成了readview,经过这个readview 这条语句将会找到符合条件的行而且计算数量。 那么关于与如何找到这些符合条件的行,知足where 条件的同时也得知足本事务对这些行的可见性。 因此在同一事务里并不会产生幻读的现象。spa
在这里咱们须要了解 当前读 和 快照读 的区别code
快照读:简单的select操做,属于快照读,不加锁。 select * from table where ?;htm
当前读:特殊的读操做,插入/更新/删除操做,属于当前读,须要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
全部以上的语句,都属于当前读,读取记录的最新版本。而且,读取以后,还须要保证其余并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其余的操做,都加的是X锁 (排它锁)。
咱们来如下倆段伪代码:
//code 1
beginTransaction
delete * from table where id = ? (加了二级索引(不是惟一索引))
endTransaction
//code 2
beginTransaction
select count(*) from table where id = ? for update (加了二级索引(不是惟一索引))
endTransaction
复制代码
delete * from table where id = ?
这是当前读,它会锁定一个范围采用GAP 锁的方式,让符合条件的范围内不得让其余事务插入数据,这样也就解决了幻读。select count(*) from table where id = ? for update
虽然也是当前读,可是它加的锁是next-key-lock,它是由GAP锁和record锁组成的,因此它也能锁定范围不让其它事务插入符合条件的数据,锁定记录自己,也不让其它事务修改数据。这样也就避免了幻读。