首页 > 代码库 > 关于ORACLE的串行化隔离级别--来自ORACLE概念手册

关于ORACLE的串行化隔离级别--来自ORACLE概念手册

为了描述同时执行的多个事务如何实现数据一致性,数据库研究人员定义了被

称为串行化处理(serializability)的事务隔离模型(transaction  isolation

model)。当所有事务都采取串行化的模式执行时,我们可以认为同一时间只有

一个事务在运行(串行的),而非并发的

 

以串行化模式对事务进行隔离的效果很好,但在此种模式下应用程序的效率将

大大降低。将并行执行的事务完全隔离意味着即便当前只存在一个对表进行查

询(query)的事务,其他事务 也不能再对此表进行插入(insert)操作了。(疑问:不是没有读取锁吗?)

总之,为了满足实际要求,我们需要在事务的隔离程度与应用的性能之间找出

一个平衡点。

 

关于这个疑问,我做了实验。证明这种说法是错误的,也可能是翻译的问题。

 

 

 技术分享

 

先执行第二个窗口,由于查询语句执行很长,此时去执行第一个窗口,发现是可以插入成功的,所以可以证明即使在串行化模式下,查询操作也不会阻塞插入操作。

 

 

 

 

符合以下特性的系统适合采用串行化隔离(serializable isolation): 

?  数据量大,但事务短小,只会更新较少数据行的数据库 

?  两个并发事务修改相同数据的概率较小 

?  运行时间相对较长的事务只执行只读操作 

 

 

在串行化隔离下,并发事务对数据库进行修改时只能顺序执行。具体来说,在

串行化隔离下,Oracle 在允许一个采用串行化隔离的事务修改某些数据行时,

需要判断在此事务开始执行之前,其他所有事务对这些数据行的修改已经被提交。

 

为了实现上述判断,Oracle 在数据块(data  block)内存储了相关的控制信息,

用于记录此块内数据行中所包含的数据是已提交或未提交的。即数据块内记录

了近期对本数据块内数据行进行了修改的所有事务及事务的状态。在一个数据

块内能够保留多少这样的记录是由 CREATE TABLE 或 ALTER TABLE 语句中的

INITRANS 参数设定的。

 

 

有些情况下,Oracle 无法获得足够的历史信息来判断某个数据行是否被一个事

务修改过。当大量事务在短时间内并发地修改同一数据块就会出现以上情况。

用户可以为可能被多个事务同时更新相同数据块的表设置较大的 INITRANS

值,以便避免上述情况。设置了较大的 INITRANS 值后,Oracle 就能为每个数

据块分配足够的空间来记录访问此数据块的事务的信息。

 

当一个串行化事务试图更新或删除数据,而这些数据在此事务开始后被其他事

务修改并进行了提交,Oracle 将报错:

 

ORA-08177: 无法进行串行化访问

 

当一个串行化事务因为无法进行串行化访问(Cannot serialize access)错误

而失败时,应用程序可以 选择以下几种处理方式: 

?  将错误发生之前的操作提交 

?  执行其他操作(执行前可以回滚到事务内的某个保存点) 

?  撤销整个事务

 

 

已提交读取隔离与串行化隔离的区别:

 

Oracle 为应用程序开发者提供了两种特性相异的事务隔离级别。已提交读取

(read committed)隔离和串行化(serializable)隔离都能实现高度的数据

一致性及并发访问能力。这两种隔离级别都能够利用 Oracle 的读一致性多版

本并发访问控制模型及独有的行级锁(row-level locking)技术,从而减少并发事务间的竞争。应用程序开发者可以使用这两种隔离级别开发符合现实要求

的应用系统。

 

 

在已提交读取隔离模式(read committed)及串行化隔离模式(serializable)

下执行的事务都采用行级锁(row-level locking)技术,他们在更新被未提交

的并发事务修改的数据行时都会发生等待--等待未提交的并发事务提交或撤

销,并释放锁。如果未提交的并发事务进行了回滚,那么无论发生等待的事务

运行在何种隔离模式下,都能修改之前被锁住的数据行,如同未提交的并发事

务不存在一样。

 

 

当导致阻塞的事务(blocking transaction)[前文中提到的未提交的并发事务]

提交并释放了锁后,运行在已提交读写模式下的等待事务就能够继续执行其中

的更新操作。而运行在串行化模式下的等待事务将出现无法进行串行化访问

(Cannot serialize access)错误,因为阻塞事务在串行化等待事务开始后更

新了后者所存取的数据。

 

 

引用完整性:

无论在已提交读取隔离模式(read committed)还是串行化隔离模式

(serializable)中,Oracle 都不会使用读取锁,即某一个事务读取的数据可

能会被其他事务更新。在应用 级(application level)进行数据库一致性检

查的事务无法确定在其执行期间所读取的数据是否同时已被修改,因为所有修

改对此事务来说是透明的。如果应用程序的代码逻辑 需要进行应用级的一致性

检查,即便采用串行化事务(serializable transaction),也不能避免数据

不一致的问题。

 

 

 

对于存在大量并发用户快速地提交事务的系统来说,应用程序设计者应该从事

务处理量及响应时间的角度评估事务处理的性能。通常来说,为一个对性能要

求高的系统选择事务隔离级别时,需要在数据一致性及数据并发处理间进行平

衡。

 

在应用层对数据库一致性进行检查的应用逻辑必须注意,在两种隔离模式下读

操作都不会阻塞写操作。

 

已提交读取隔离:

 

对于大多数应用来说,已提交读取隔离(read committed)是最适合的事务隔

离级别。已提交读取隔离能够最大限度地保证数据并发性,但在某些事务中可

能会出现不可重复读取(non-repeatable read)或不存在读取(phantom),

因此略微增加了出现数据不一致性的风险。

 

在对性能要求较高的系统中, 为了应对较高的事务到来率 (transaction arrival

rate),系统需要提供更大的事务吞吐量和更快的响应速度,此时采用串行化

隔离可能难以实现。还有一类系统,其事务到来率较低,出现不可重复读取或

不存在读取的风险也较低。以上两种系统均适合采用已提交读取隔离

 

在已提交读取隔离模式下,开发者不需要在应用逻辑中捕获无法进行串行化访

问(Cannot serialize access)错误,也无需回滚并重新执行事务。在大多数

应用程序中,几乎不会有在一个事务中执行同一查询多次的情况,因此在这些

应用程序中,为防止出现不可重复读取或不存在读取而采取的保护措施并不重

要。 如果开发者选择已提交读取隔离,就能够省略在每个事务中加入错误检查

及事务重做的代码。

 

串行化隔离:

 

 

Oracle 的串行化隔离(serializable  isolation)适合于具备以下特点的系统:

出现修改相同数据的事务的几率较小,且长时间执行的事务以只读操作为主。

最适合采用串行化隔离的系统是大型数据库,且其中主要运行更新少量数据的

短小事务。

 

串行化隔离能够提供更好的数据一致性,她能阻止不可重复读取

(nonrepeatable read)或不存在读取(phantom)的现象。当一个读或写事务

中需要运行同一查询多次时,串行化隔离的作用更加明显。

 

某些数据库管理系统在实现串行化隔离时,无论读写操作都要对整个数据块加

锁。而 Oracle 则采用了无阻塞查询(nonblocking query)及低粒度的行级锁

技术(row-level locking),减少了读写操作间的竞争。对于存在较多读写竞

争的应用,Oracle 的串行化隔离与其他数据库管理系统相比能够大大地提高事

务处理能力。因此,某些应用在 Oracle 中可以采用串行化隔离,而在其他数

据库管理系统则未必可行。

 

运行在串行化隔离模式下的事务中的所有查询所获得的数据都来自同一时间点

(single point in time),因此这种隔离级别适合于需要执行多个满足一致

性的查询的事务。例如,汇总数据并将结果写入数据库的报表应用可以采用串

行化隔离,因为串行化事务所提供的数据一致性与 READ ONLY 事务相同,但其

中还可以执行 INSERT,UPDATE,和 DELETE 操作。

 

 

如果事务中存在使用了子查询的 DML 语句,应该使用串行化隔离来保证一致性

的读取。

在实现串行化隔离事务时,应用开发者必须编码来捕获无法进行串行化访问

(Cannot serialize access)错误,之后回滚并重做事务。在其他数据库管理

系统中需要类似的代码来处理死锁(deadlock)。有时为了遵从已有系统的标

准,或者当应用可能运行在多种数据库管理系统上时,应按照串行化隔离的要

求来设计事务处理代码。具备检查并处理串行化错误功能的事务也可以运行在

Oracle 的已提交读取(read committed)隔离模式下,因为此种隔离模式下不

会产生串行化错误。

 

 

如果系统中存在长时间运行的写事务,且其所操作的数据同时还会被大量的小

事务更新,则此类系统不应采用串行化模式。因为长事务所需更新的数据可能

会被其他事务抢先更新,则长事务可能需要重复地回滚,浪费系统资源。需要

注意的是,其他数据库管理系统所实现的串行化隔离(使用读取锁

(read-locking))同样不适合上述情况,因为长事务(即便是只执行读取操

作的事务)会和短小的写事务相互阻塞。

 

 

应用开发者在采用串行化隔离时应考虑回滚及重做事务所带来的开销。而在采

用读取锁的数据库管理系统中,死锁会频繁出现,在这样的系统中采用串行化

隔离,必须 令因死锁而终止的事务回滚并重做。如果一个系统对数据访问的竞

争较激烈,那么处理串行化错误将消耗大量资源。

 

 

在大多数系统中,一个事务发生无法进行串行化访问(Cannot serialize

access)错误后重做时再次与其他事务冲突的几率较小。基于上述原因,采用

串行化隔离时,容易与其他事务产生竞争的语句应该在事务开始后尽早执行。

但是我们始终无法保证事务能够成功执行,因此在应用程序中应该限制重做的

次数。

 

 

尽管 Oracle 的串行化隔离模式与 SQL92 兼容,而且与采用读取锁的实现方式

相比具备很多优点,但是 Oracle 串行化隔离所包含的语义与其他数据库管理

系统不完全相同。 应用开发者必须注意, 与其他数据库管理系统不同, 在 Oracle

中读操作不会阻塞写操作。在应用级(application level)对数据库一致性进

项检查的事务需要使用 SELECT FOR UPDATE 之类的技巧才能避免错误。当从其

他数据库管理系统向 Oracle 迁移应用时,尤其要注意上述问题。

 

 

综上所述,我理解ORACLE的串行化模式,就是两点:

事务级读一致性。

在事务开始的点开始,就会独占当前事务里所涉及的所有更新操作,拒绝其他事务来对独占的数据进行更新。如何保证这点呢,分两种:首先,事务在开始的时候回判断是否存在未提交的更新(可能会出现判断遗漏),如果没有才会继续,其次,举例,一个事务已经执行了一半,一种是当前事务已经执行的更新操作,由于没有提交,其他事务肯定只能等待,不能修改,另外一种是当前事务还没有执行的更新操作,在等到执行这段更新代码的时候,会去查看其他事务是否在当前事务开始后执行了提交(针对上面判断遗漏的情况),如果有,那么将抛出错误。

 

所在,这种方式实现了,事务的串行执行。串行执行并不是说,当前数据库中的所有事务都会串行排队,而只是针对事务中所涉及的更新数据,不会与其他事务并行修改。

 

关于ORACLE的串行化隔离级别--来自ORACLE概念手册