论文地址:对ANSI-SQL隔离级别的评论

    ANSI SQL隔离级别最初是以散文的形式定义的,它是根据三个特定的异常来设计的,这些异常是为了防止的。不足为奇的是,这些最初的定义有些模棱两可,而且可以接受严格和广泛的解释。这也证明了它们是不够的,因为标准中还有一个甚至更基本的异常没有提到,为了能够实现回滚和恢复,需要加以预防。更深入地看,论文揭示了八种不同的现象(异常)可能发生,六种不同的隔离水平。在这篇文章中,我将着重解释这些现象和隔离级别,对于那些对原始ANSI SQL隔离级别的详细比较感兴趣的人,我将向您介绍全文。
    (我希望你觉得这篇文章中所有的历史草图都很有用——我花了很长时间才画出来并浏览完它们!您可能会发现查看稍大版本的图像更容易–只需单击图像即可查看放大的视图。)
    这是一张总览图,显示了它们之间的关系,这可能会对我们的工作有所帮助。
    ansi-sql-isolation-levels.png

    现象/异常
    在下面的描述中,长持续时间锁是那些在事务结束之前一直保持的锁,而短持续时间锁则是在所涉及的操作完成后释放的锁。此外,
    如果事务在写入(读取)该数据项或由谓词定义的一组数据项之前请求对每个数据项或谓词进行写(读)锁,则该事务具有格式良好的写(读)操作。如果事务具有格式良好的写入和读取,则它是格式良好的。如果事务在释放写(读)锁后未对数据项设置新的写(读)锁,则该事务具有两阶段写(读)操作。如果事务在释放某个锁后不请求任何新锁,则它将显示两阶段锁。

    P0:脏写
    当一个事务重写先前由另一个仍在运行的事务写入的值时,会发生脏写。
    脏写不好的一个原因是它们会破坏数据库一致性。假设x和y之间有一个约束(例如,x=y),如果单独运行,T1和T2各自保持约束的一致性。但是,如果两个事务以不同的顺序写入x和y,则很容易违反约束,而只有在有脏写入时才会发生这种情况。
    假设T1写入x=y=1,T2写入x=y=2,则以下历史记录违反完整性约束。

    dirty-write.png

    防止脏写的另一个紧迫原因是,如果没有对它们的保护,系统就无法在事务中止时自动回滚到before映像。
    即使是最弱的锁定系统也会保持长时间的写锁…
    (结构良好的写操作和长时间的写锁可防止脏写操作)。
    除可序列化级别外,ANSI SQL隔离级别不排除脏写操作。但是,“应该修改ANSI SQL,使其要求对所有隔离级别执行P0(保护)。”

    P1:脏读
    当一个事务读取另一个仍在运行的事务写入的值时,会发生脏读。仅防止读取由最终回滚的事务写入的值是不够的,还需要防止从最终提交的事务读取值。考虑x和y之间的平衡转移,完整性约束条件是平衡保持在100:
    dirty-read.png
    **
    在基于锁定的实现中,结构良好的读写结合持有短期读锁和长期写锁将防止脏读。

    P2:模糊读取(不可重复读取)
    当一个仍在运行的事务读取的值被另一个事务覆盖时,会发生模糊或不可重复的读取。即使没有对实际发生的值进行第二次读取,这仍然会导致违反数据库不变量。从我们的转移示例中考虑另一段历史:
    fuzzy-read.png

    在锁定实现中,结构良好的读写结合了数据项的长时读写锁,但只有基于谓词的读的短时锁才能防止不可重复的读。

    P3:幻影
    当一个事务执行基于谓词的读取(例如SELECT…WHERE P)并且另一个事务在第一个事务仍在运行时写入与该谓词匹配的数据项时,就会出现幻象。在最初的ANSI SQL语言中,只禁止匹配的插入(新的条目是幻影),但实际上为了安全起见,我们需要禁止任何写操作:更新、删除和插入。请考虑以下历史:
    phantom.png

    在基于锁的实现中,结构良好的读写加上长时读锁(数据项和谓词)和长时写锁将防止出现幻象。

    P4:丢失更新
    事实证明,存在一种异常,这种异常不是由防御脏读和脏写的系统所阻止的,而是由同时防御模糊读的系统所阻止的。这就是所谓的丢失更新。请考虑以下历史:
    lost-update.png

    在一个锁定实现中,在高于防止P0和P1的要求时,在光标移动或关闭之前对游标的当前项保持锁定将防止游标丢失更新。

    A5A:读取倾斜
    当两个或多个数据项之间存在完整性约束时,可能会发生读取倾斜。考虑约束x+y=100和历史:
    read-skew.png

    当T1读取y时,它看到一个不一致(倾斜)的状态,因此可能会生成一个不一致的状态作为输出。

    A5B:写入倾斜
    写倾斜是非常相似的。假设我们的约束条件是x+y≤100。这次,我们在T1发出写操作之前插入一个事务:
    write-skew1.png
    隔离级别
    我们可以通过它们允许(防止)的现象来描述隔离级别:

    isolation-levels-table.png

    我们可以根据它们允许的非序列化历史记录来比较隔离级别:
    如果L1允许L2不允许的不可序列化历史,则L1比L2弱,并且L2下的每个不可序列化历史也是L1下的不可序列化历史。我们写L1<如果L1和L2所允许的不可序列化历史的集合相同,则它们是等价的。我们写L1==L2。
    L1和L2也可能是不可比拟的。如果L1允许L2不允许的非序列化历史,反之亦然,则L1不弱于L2,但L2也不弱于L1。我们写L1<>L2。
    我们可以根据<<:
    0级(一切正常)<本文还讨论了另外两个隔离级别:Oracle一致读取和快照隔离。与游标稳定性一样,Read Committed<<Oracle Consistent Read。Oracle读取一致性隔离为每个SQL语句提供了在语句开始时最新提交的数据库值。这就好像事务的开始时间戳在每个SQL语句处是高级的一样。光标集的成员是打开光标时的成员。底层机制从语句时间戳开始重新计算行的适当版本。行插入、更新和删除由写锁覆盖,以使第一个写入程序获胜,而不是第一个提交程序获胜策略。

    相反,在快照隔离中,
    …每个事务从事务启动时(提交的)数据的快照(称为其开始时间戳)中读取数据。此时间可能是事务首次读取之前的任何时间。只要快照数据的起始时间戳可以维护,在快照隔离中运行的事务在尝试读取时就不会被阻止。事务的写入(更新、插入和删除)也将反映在此快照中,以便在事务第二次访问(即读取或更新)数据时再次读取。在事务开始时间戳之后由其他活动事务进行的更新对事务不可见。
    快照隔离和Oracle一致读取都是多版本并发控制机制的类型,在这种机制中,我们必须对同一数据项在任意给定时间点的多个不同值进行推理。结果表明,所有快照隔离历史记录都可以映射到单值历史记录,同时保留数据依赖关系。
    已提交读取<<快照隔离<<可序列化。
    但是快照隔离和可重复读取是不可比拟的。快照隔离允许写歪斜,而可重复读取不允许写歪斜,可重复读取允许某些在快照隔离下不允许的幻象。