笔者近在回顾多版本并发控制(MVCC),这里对相关内容做个小总结,首先就从隔离级别说起。首先,作为事务系统,如果单位时间内,只有一个事务在运行,则谈不上隔离。可是实际上,真正用于实际生产的事务系统,单位时间内都是有多个事务同时的在对资源进行着读写。那么就必定会存在对同一个资源的并发读写问题,如果不加以控制则一定会出错。因此这里讨论问题的场景就是多事务并发环境下得数据处理场景。什么样的事务系统是合理的,正确的,或者说能够准确地对现实世界的逻辑进行抽象的?满足 ACID 特性。满足 C 就要实现 I,因此隔离的目的是为了满足 C。然而要回答为何要对隔离进行分级,我们得先从不满足 C 的异常场景得拆解来入手分析下。何为更新丢失?两个并发的事务T1、T2,不加任何锁的对对同一块资源进行写操作。T1 更改的结果被 T2 事务篡改,这就是更新丢失。直白点就是没有任何隔离手段,纯并发。这种场景,不能出现在任何合理的应用系统中,属于基础重大错误。脏读的定义,就是读取到了脏数据。这里首先明确什么是脏数据。在事务系统中,脏数据是指未提交的数据,中间状态的数据。那么读取到了事务没有提交的数据,就是读取到了脏数据,称为脏读。可以用下面的图来表示:有两个事务 T1 T2 同时存在,T1 更改了 a 的值,在没提交的时候,T2 读取到了 a 被 T1 更改后的值。如果此时,T1 回滚了,那么 T2 读取到的 a 的值,就是一个不被认为存在的值,或者是读取到了事务的中间状态,这就是脏读。我们可以简单的用加锁的方法来理解即:这里所有事务在对临界资源的访问上,只加了读锁。这意味着,其他的事务可以在任意时刻读取未提交的数据(因为读锁是兼容的),但是不能写已经被修改的数据(因为读锁和写锁不兼容)。因此在脏读的场景下,一般默认是认为,更新丢失的问题已经得到了解决。
3、Un-repeatable Read / Read skew 不可重复度 / 读偏序不可重复读的指的场景是,事务T1读取到的数据因其他事务T2的修改并提交后,当T1再次读取时,出现了不同的结果。我们这里首先看下,不可重复读和脏读的区别是:不可重复读,读取到的所有的结果,都是已经提交的数据,脏数据看不到了。可以用下面的图来表示下:图片来源于网络
通过上图我们简单理解,得以实现不可重复度与脏读区别的,是在 T2 事务准备写数据的时候,实现了这里的读锁向写锁的升级,使得 T1 无法读取 T2 事务提交前的数据值,但是当 T2 完成释放了所有的锁后,T1 再次读取数据时,已经是被修改后的版本。幻读指的是,T1 事务在执行过程中的不同时间点,以相同的谓词条件获得的结果集不同,这里只限定为多出了数据,而不是数据不同。这里我们需要明确的是,幻读相对于上面提到的不可重复度的区别是:幻读场景下,已经没有不可重复读的问题,即两次查询相同主键的值相同,只是多出了部分记录(为什么不是少了数据?)。可以用下面的图来表示:图片来源于网络
我们可以简单的理解为,事务 T1 在对所要操作的资源上,只读的数据加读锁,要修改的数据加写锁,并且都是事务提交后释放。因此其他的事务无法对相同的资源进行修改,因此在幻读的场景下,不可重复度的情况不会发生。但是 T1 却无法对新产生的数据行加锁,因此在这里体现了幻读的与不可重复度的区别即相同谓词的结果集不同,一般指后一次的结果集是前一次结果集的超集。从上面的分析拆解,以及从加锁的角度来理解这几个异常场景,可以发现一个递进关系:在解决了上一个问题时,又发现了下一个新问题。因此才引出了,根据这几个异常场景来定义的不同的有递进关系的隔离级别的概念。在下篇章<事务系统的隔离级别>的分析中,我们依旧可以发现,每个隔离级别,解决一个层次的问题。敬请期待~