天天看点

理解《A Critique of ANSI SQL Isolation Levels》

重新定义隔离级别

如题,这篇论文是对于ANSI/ISO SQL-92 中规定的四种隔离级别(READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE)的批判。在ANSI的定义中,每种隔离级别分别解决了不同的异象(Dirty Read, Non-repeatable Read, Phantom)

  • A1 脏读:w1[x] … r2[x] … (a1 and c2 in any order)
  • A2 不可重复读:r1[x] … w2[x] … c2 … r1[x] … c1
  • A3 幻读:r1[P] … w2[y in P] … c2 … r1[P] … c1

排除了所有异象事务就可以串行化执行。然而,ANSI定义的异象并没有包括所有非串行化的行为。对于某些不同数据(two-object)上的读写操作,数据间的约束无法保证。

H1: r1[x=50]w1[x=10]r2[x=10]r2[y=50]c2 r1[y=50]w1[y=90]c1

H2: r1[x=50]r2[x=50]w2[x=10]r2[y=50]w2[y=90]c2r1[y=90]c1

H3: r1[P] w2[insert y to P] r2[z] w2[z] c2 r1[z] c1

H1模拟了简单的转账事务,T1将x扣除40,T2将y增加40,(x,y)间的约束是x+y=100,然而T2读到的和确是60。r2读未提交数据的行为不符合A1的形式却依然导致了非串行化的结果。同理,H2对应A2,H3对应A3都没有形式上匹配。

因此,论文给出了相对于A1/A2/A3更宽泛的定义P1/P2/P3用于排除H1/H2/H3等非串行化行为。并新增异象p0(脏写)

  • P0脏写: w1[x]…w2[x]…(c1 or a1) (Dirty Write)
  • P1脏读: w1[x]…r2[x]…(c1 or a1) (Dirty Read)
  • P2不可重复读: r1[x]…w2[x]…(c1 or a1) (Fuzzy or Non-Repeatable Read)
  • P3幻读: r1[P]…w2[y in P]…(c1 or a1) (Phantom)

对应新的定义,为了阻止每种异象的发生,每个隔离级别对应了一种锁的实现:

  • Read Uncommitted,阻止P0:整个事务阶段对x加长写锁
  • Read Commited,阻止P0,P1:短读锁 + 长写锁
  • Repeatable Read,阻止P0,P1,P2:长读锁 + 短谓词锁 + 长写锁
  • Serializable,阻止P0,P1,P2,P3:长读锁 + 长谓词锁 + 长写锁

然而,新的定义也并非完美,因为禁止的更加定义更加宽泛的异象,事务的锁机制造成会“误杀”。举一个简单的例子

H’: r1[x=50]w2[x=10] c1 c2

在 REPEATABLE READ 的隔离级别下,w2是要等待T1提交的,然而不等待也不会造成非串行化的结果。

Snapshot Isolation

锁的实现机制在事务发生读写、写读冲突时的等待会对吞吐率和cpu性能会造成较大浪费,snapshot机制解决了这一问题。snapshot机制本质上是将事务开始时间戳之前的所有已提交的事务当作一个快照,每个事务的读写都在独立的快照副本中执行,互不影响,因此也不会造成冲突的等待。然而snapshot依然无法到达可串行化执行的级别,来看两个snapshot级别下的异象:

A5A读偏斜: r1[x]…w2[x]…w2[y]…c2…r1[y]…(c1 or a1) (Read Skew)

发生读偏斜的本质是,同一个事务的两次读(不同数据)读到的快照版本不一样,r1[x]读到的版本为T1开始时间戳之前的版本,而r1[y]读到的是T2已提交的快照版本,这样可能会破外某种约束(x+y>0),而且两次写的不是同一条数据,不会触发 First-Committer-Wins 的重试机制。

A5B写偏斜: r1[x]…r2[y]…w1[y]…w2[x]…(c1 and c2 occur) (Write Skew)

上述的只是写偏斜的一种情况,写偏斜的本质是rw读写的冲突,r1[x]读到的是w2[x]写之前的数据,从这个依赖上来看T1比T2先发生(T1<<T2),而r2[y]读到的是w1[y]写之前的数据,从这个依赖上看 T2<<T1,所以事务冲突。

那么si既然无法达到 Serializable ,那么它属于哪个隔离级别呢?论文中尝试将si与基于锁机制的隔离级别进行对比,参考另一篇优秀文章 的分析

快照隔离性高于Read Committed:第一,考虑到快照隔离读已提交的数据版本的特性,禁止了P1,因此保证至少不低于Read Committed。第二,A5A的Read Skew异象符合P2的定义,并且从一致性的角度分析,事务1对x和y的读取的两个值不在线性的历史中,可能会违背某种外部约束(比如保证x+y的和为一个常量),因此Read Committed隔离级别下允许A5A Read Skew异象。总和以上两点,我们可以得出结论,快照隔离性高于Read Committed。

快照隔离性与Repeatable Read不相容:考虑到快照隔离能够保证读取到的数据在一个一致的历史快照上,禁止了P1/P2/P3,因此保证不低于ANSI的Anomaly Serializable级别;但是,另一方面,经典的快照隔离对于多写冲突是基于First- committer-wins的处理方式,依赖冲突的事务间至少修改同一条记录(现代快照隔离有更优的SSI,我们将在后续的文章中介绍)无法避免上述A5B Write Skew的两种异象,而基于锁事项的Repeatable Read级别却可以禁止A5B。快照隔离与Repeatable Read双方禁止的异象,有可能在对方出现,因此他们的隔离性无法相比较。

综上,si的隔离强度:Read Uncommitted < Read Committed < (Repeatable Read >< Snapshot) < Serializable