2.1 读未提交

“事务隔离级别”是指DBMS内部机制(即不需要特殊编程)对事务并行执行过程中出现的上述所有或部分类型的数据不一致的保护程度。SQL-92 标准定义了四个隔离级别的范围:

  • 读取未提交
  • 读提交
  • 可重复读
  • 可序列化

第一个是最弱的,最后一个是最强的,后面的每一个都包含了前面的所有。

最低(第一)隔离级别。如果多个并行事务试图修改同一个表行,那么最后一行的值将由整个成功完成的事务集决定。在这种情况下,不仅可以读取逻辑上不一致的数据,还可以读取尚未记录更改的数据。

实现此隔离级别的典型方法是在执行更改命令时锁定数据,这可确保并行运行的相同行上的修改命令实际上是按顺序执行的,并且不会丢失任何更改。只读事务永远不会在此隔离级别下阻塞。

2.2 读提交

大多数工业 DBMS,特别是 Microsoft SQL Server、PostgreSQL 和 Oracle,默认使用此级别。在这个级别,提供了防止草稿、“脏”读的保护,但是,在一个事务的运行过程中,另一个事务可以成功完成,并且它所做的更改是固定的。因此,第一个事务将使用不同的数据集。

完整读取的实现可以基于以下两种方法之一:阻塞或版本控制。

阻塞可读和可变数据。

它包括这样一个事实,即写入事务阻塞可变数据以读取在读取提交级别或更高级别操作的事务,直到它完成,从而防止“脏”读取,并且读取事务锁定的数据在操作完成后立即释放SELECT(因此,在给定的隔离级别上可能会出现“不可重复读取”的情况)。

保存并行更改的多个版本的行。

每次更改一行时,DBMS 都会创建该行的一个新版本,更改数据的事务将继续使用该行,而任何其他“读取”事务将返回最后提交的版本。这种方法的优点是速度更快,因为它可以防止阻塞。但是,与第一个相比,它需要更多的 RAM 消耗,这些 RAM 用于存储行版本。

此外,当多个事务并行更改数据时,可能会造成多个并发事务对同一数据进行不一致更改的情况(因为没有锁,所以没有什么可以阻止这种情况的发生)。那么最先提交的事务会将其更改保存在主数据库中,其余并行事务将无法提交(因为这会导致第一个事务的更新丢失)。在这种情况下,DBMS 唯一能做的就是回滚其余事务并发出错误消息“记录已被更改”。

具体的实现方法由 DBMS 开发人员选择,在某些情况下可以定制。因此,默认情况下,MS SQL 使用锁,但(在 2005 及更高版本中)在设置数据库参数时READ_COMMITTED_SNAPSHOT,它切换到版本控制策略,Oracle 最初仅根据版本控制方案工作。USELASTCOMMITTED在 Informix 中,您可以通过设置一个配置选项(自版本 11.1 起)使读取事务接收最新提交的数据来防止读写事务之间的冲突。

2.3 可重复读

读取事务“看不到”的级别改变了它之前读取的数据。同时,任何其他事务都不能改变当前事务读取的数据,直到它结束。

共享模式下的锁应用于事务中任何指令读取的所有数据,并一直保持到事务完成。这可以防止其他事务修改挂起事务读取的行。但是,其他事务可能会插入与当前事务中包含的指令的搜索条件相匹配的换行符。当前事务重新启动该语句时,将获取新行,从而导致幻读。

鉴于共享锁一直持有到事务结束,而不是在每条语句结束时释放,因此并发度低于隔离级别READ COMMITTED。因此,一般不建议不必要地使用这个和更高的事务级别。

2.4 可序列化

最高级别的隔离;事务彼此完全隔离,每个事务都像没有并行事务一样执行。只有在这个级别,并发事务才不会受到“幻读”的影响。

2.5 支持真实DBMS中的事务隔离

事务型 DBMS 并不总是支持所有四个级别,还可能引入其他级别。在提供绝缘方面也有各种细微差别。

所以,Oracle原则上是不支持零级别的,因为它对事务的实现排除了“脏读”,并且正式不允许设置Repeatable read级别,即它只支持(默认)Read committedSerializable。同时,在单个命令的层面上,它实际上保证了读取可重复性(如果SELECT第一个事务中的一个命令从数据库中选择了一组行,此时并行的第二个事务更改了其中的一些行,那么第一个事务收到的结果集将包含未更改的行,就好像没有第二个事务一样)。Oracle 还支持所谓的READ-ONLY事务,它对应于Serializable,但不能更改数据本身。

并且Microsoft SQL Server支持所有四种标准事务隔离级别,此外还支持 SNAPSHOT 级别,在该级别事务可以看到在它启动之前提交的数据状态,以及它自己所做的更改,即它的行为如下如果它在启动时接收到数据库数据的快照并使用它。与 Serialized 的区别在于没有使用锁,但是如果并发事务之前更改过相同的数据,则可能无法提交更改;在这种情况下,第二个事务在尝试执行时COMMIT将引发错误消息并被取消。