笔者近在回顾多版本并发控制(MVCC),上篇章从加锁的角度理解了几个异常场景(有兴趣的朋友可以点击查看「技术讨论」事物系统的几种异常场景),由此可以发现一个递进关系:在解决了上一个问题时,又发现了下一个新问题。因此才引出了,根据这几个异常场景来定义的不同的有递进关系的隔离级别的概念。
在对事物系统的隔离级别分析中,我们同样可以发现:每个隔离级别,是用来解决相对一个层次的问题。
1、Read-Uncommited 读未提交
该隔离级别解决个层面的异常场景:更新丢失。该隔离级别,可以读到任意时刻的数据,但是对未提交的其他事务正在修改的数据,无法同时写。我们也可以理解,在这样一个隔离级别下,对资源的所有的操作,加了读锁。如图1所示:
该隔离级别,解决第二个层面的异常场景:脏读。该隔离级别,使得未提交的数据不能被读取。可以理解为,在该隔离级别下,对要修改的数据,读锁升级成了写锁,导致其他事务无法对该数据进行读取,因而保证了不会出现脏读的问题。如图2所示:图2
该隔离级别,解决第三个层面的问题:不可重复读。该隔离级别下,一个事务对所涉及到的数据,不论是读操作还是写操作,一律加写锁,因而保证了其他的事务,既不能读取,也不能修改这部分数据,因此确保了在事务执行过程中,这些被锁定的数据,不会被其他的事务修改,确保不会出现不可重复读的问题。如图3所示:
该隔离级别下,解决第四个层面的问题:幻读。因为一个事务即便是对所有要读写的数据全部加写锁来实现彻底的排他,但是无法预知哪些数据是会新产生的,因此要确保不产生幻读的情况,的做法就是实现串行化事务,没有并发,实现根本性的事务隔离,前序事务未结束,后序事务不得开始。如图4所示:上述对事务隔离级别的描述,可以总结为以下的图,就是一个逐层递进,逐层解决的过程。如图5所示:图5
从上面的图和分析中,我们考虑问题的角度都是一般性的事务系统,不涉及到具体的实现,站在事务系统的角度来说,要解决幻读的问题,需要实现串行化的隔离级别。但是MySQL在 rr的隔离级别,已经实现了对幻读的避免。这是为什么呢?为何MySQL可以在不实现串行化的情况下,完成对幻读问题的解决?这里的原因与MySQL-innoDB 的数据组织形式密切相关:因为 innoDB 的数据时有序存放的。这里的有序存放就带来了一个潜在的事实:相同谓词所能够匹配到的数据,一定时物理上存放在相邻的位置上的。基于这样的事实,MySQL通过一种新的类型的锁“间隙锁”来保证无法产生幻读,就是因为多锁了那一部分间隙,导致新增的数据,无法写入被锁住的间隙,因此确保了不会出现幻读。