总所周知,Mysql中有四种隔离级别:读未提交、读已提交、可重复读、序列化。这四种隔离级别的产生来自于对于数据读取过程中的一些错误的解决。错误主要分为这三类:脏读、可重复读、幻读。
这里对脏读、可重复读、幻读做一下简单地概述:
脏读:读到了未提交的数据,这一般是在读未提交隔离级别下会出现的问题。
不可重复读:同一条执行语句,第一次和第二次执行所得到的的解决不同,一般是发生在读已提交和读未提交隔离级别下
幻读:幻读主要指的是前后两次执行相同的语句,得到的结果的数量不同,读未提交、读已提交、可重复读都会出现幻读的问题。
对于出现脏读的情况,可以将隔离级别设置为读已提交,出现不可重复读可以将隔离级别设置为可重复读。这两种机制的隔离级别会利用到MVCC,我们分别来看一下MVCC是如何实现这两种隔离级别的。
这里需要介绍一下ReadView这个概念,事务会在启动的时候拍下快照,这个快照就是readview。读分为两种,分别是快照读和当前读,
快照读:简单的select操作(不包括 select ... lock in share mode, select ... for update)。
当前读:当执行select...lock in share mode (共享读锁)、select...for update、update , delete , insert时,这时的快照是当前读。
MVCC只能在快照读下实现。
对于Readview,它由这几部分组成:
m_ids | min_trx_id | max_trx_id | creator_trx_id |
---|---|---|---|
当前活跃事务编号 | 最小活跃事务编号 | 预分配事务编号,当前最大事务编号+1 | ReadView创建者的事务编号 |
版本链的访问规则如下:
在读已提交和可重复读的下,版本连的访问规则如下:
1、如果被访问版本的trx_id,与readview中的creator_trx_id值相同,表明当前事务在访问自己修改过的记录,该版本可以被当前事务访问;
2、如果被访问版本的trx_id,小于readview中的min_trx_id值,表明生成该版本的事务在当前事务生成readview前已经提交,该版本可以被当前事务访问;
3、如果被访问版本的trx_id,大于或等于readview中的max_trx_id值,表明生成该版本的事务在当前事务生成readview后才开启,该版本不可以被当前事务访问;
4、如果被访问版本的trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中?
如果在:说明创建readview时生成该版本的事务还是活跃的,该版本不可以被访问
如果不在:说明创建readview时生成该版本的事务已经被提交,该版本可以被访问;
假设有多个事务如下对数据库修改
总结来说就是在访问数据的时候,查找版本连readview,如果被访问者的trx_id小于min_最小的活跃事务编号,那么该版本就能被当前事务访问,如果大于或等于最大事务编号就不能被访问,如果在最大值和最小值之间,那么不是活跃的就能被访问,活跃的不能被访问。
从上图1我们可以发现,在读未提交的情况下,仍然会出现不可重复读的现象,而MVCC解决不可重复读的方法是复用readView来实现。而在可重复读隔离界别加幻读仍然会发生,幻读的本质是是在两次快照读之间存在当前读,readview会重新生成,导致了幻读。而对于幻读的解决可以通过航所加间隙锁来实现。