可重复读隔离级别会出现幻读吗
放在最前面:InnoDB的默认事务隔离级别是可重复读
隔离级别
MySQL的隔离级别包括四种:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。
- 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
- 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
- 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 串行化,对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
前两种隔离级别,是一定会出现幻读。而可重复读隔离级别是采用MVCC,即一致性视图来解决不可重复读的问题。具体而言,就是采用快照的方式,事务开启的时候会有一个版本号,然后数据是否可见都是基于版本号来判断的,低于该版本号的数据可见,高于的都不可见。
而可重复读隔离级别下,如果事务A开启后,事务B做了一定的修改并提交事务,在事务A还未提交的时候,对事务B提交的数据做了修改,这个修改是在事务B提交数据的基础上进行修改的,因为不这样的话会导致事务B提交的数据丢失。那也就意味着此次修改会修改数据的事务版本号,后续进行查询的时候,该版本号就可能会导致幻读的问题,以及查询结果与认为中的不一致问题。这也是为什么在可重复读隔离级别下仍然会出现幻读的问题。
上述问题有一个原因:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。当前读意味着会读取最新的数据。
其实除了Update语句外,select语句如果加了锁,即for share
或者for update
也是当前读。
实现可重复读的核心是一致性读,而更新数据必须使用当前读,这也是为什么会失效的原因。
幻读
在可重复读的隔离级别下,普通读都是快照读,是看不见其他事务修改的数据的,幻读的问题只会发生在当前读。即select for update
或者select for share
这种。
幻读是指在一个事务(假定事务A)的两次查询中,第二次查询查到了第一次没有的数据。发生幻读的根本原因是另外的事务(假定事务B)添加或修改了数据,该数据正好满足事务A的查询条件。
结论
可重复读的隔离级别,在某些场景下是无法防止幻读的问题,需要用到间隙锁来解决。