首先,我们需要搞懂几个隔离级别的意思和每个隔离级别会出现的问题。隔离级别分为:读未提交,读提交,可重复读和可串行化。
读未提交是最低级别的隔离级别,表示当一个事务还没有提交时,他所做的变更就被别的事务读取到;在大部分场景下这显然是不允许的;
为了解决读未提交出现的问题,会将隔离级别设置为读提交,意思是只有一个事务被提交了他做的改变才会被别的事务看到;
但是读提交级别下还是会出现问题,最经典的场景就是银行转账,有这样2个事务A,B同时进行,A事务中账户拥有者将自己的钱从X账户转到Y账户,当钱还没有转到Y账户时,B事务查询了Y账户的钱,这时候事务A结束,钱已经从X账户扣除转给了Y账户,因为是读提交级别,事务提交了,修改也可见了,B事务继续查询自己X账户的钱,因为数据更新,在B事务中用户看到的情况是自己的总金额少了,这就出现了不可重复读的问题。
为了解决不可重复读的问题,出现了可重复读隔离级别,innodb使用MVCC解决了不可重复读的问题,也就实现了可重复读的隔离级别,MVCC机制是为每个事务提供快照隔离,事务执行期间对数据的可见性是有要求的,事务执行期间读到的数据和事务开始执行时的数据是一致,关于可见性有这样的原则:在事务A开始执行时,如果其他的事务B提交了那么数据更改是可见的,如果在事务A执行期间,其他事务B提交了,那么事务B所做的数据更改是不可见的。MVCC的实现原理是基于undo log实现。undo log是用类似链表的方式实现,当事务在RR级别会使用undo log推算当前视图下的数据。
但是在可重复读隔离级别下,还是会出现问题,那就是幻读,当事务在执行期间读数据会分为两种情况,一种是快照读,在这种情况下,读到的数据就是快照数据,看不到其他的数据更改,另一种是当前读,这种情况下会读到更新的数据,这时会对表加上行锁,对于数据(1 2 4 5),当查询小于4的数时候,会读到(1 2),这时候会对1 2加锁,保证数据不会被修改,但是如果其他事务插入了一个3,当事务再更新读的时候就会读到(1 2 3),因为3这个数之前没有出现过也就没法锁住,这时候就出现了幻读,在一个事务的两次当前读的时候出现数据不一致的情况就是幻读。因为没法锁住不存在的行。
为了解决这个问题,innodb使用next-key锁实现,next-key锁=行锁+间隙锁,而间隙锁就是用来锁住这些不存在的数据,锁住(2,3)这个间隙,保证不会被插入新的数据。所以就解决了幻读的问题。
总之,在标准的可重复读(RR)是无法解决幻读问题的,MySQL比较特殊在RR隔离级别也解决了幻读的问题,因为MySQL默认使用innodb作为存储引擎,而innodb使用next key锁解决了幻读的问题。
因为有些东西记不准了,有问题欢迎提出。