包含下列主题: u 多用户环境中的数据并发性和一致性介绍 u Oracle如何管理数据并发性和一致性 u Oracle如何锁定数据 u Oracle闪回查询概述 多用户环境中数据并发性和一致性介绍 在单用户数据库中,用户修改数据库中的数据,不用担心其他用户同时修改相同的数据。但是,在多用户数据库中,同时执行的多个事务中的语句可以修改同一数据。同时执行的事务需要产生有意义的和一致性的结果。因而,在多用户数据库中,数据并发性和数据一致性的控制非常重要: u 数据并发性:指很多用户可以同时访问数据 u 数据一致性:指每个用户可以看到数据的一致性结果,包括自身事务和其他事务产生的改变。 为给同时运行的事务描述一致性事务行为,Oracle研究员定义了一个事务隔离级别:串行性(serializability)。事务行为的的可串行模式试图确保事务运行的方式看起来像是一次运行一个事务(或者串行性),而不是并发运行。 虽然事务间的这种隔离级别经常使用,但这种模式下的很多应用程序会降低吞吐量。并发运行事务的完全隔离意味着一个事务不能对另外一个事务正在查询的表执行插入。简而言之,真实环境中经常要在优秀的事务隔离级别和性能之间做一个折衷。 Oracle提供了两个隔离级别,为应用程序开发人员提供可选的模式来兼顾性能和一致性。 可预防现象和事务隔离级别 ANSI/IOS SQL标准(SQL 92)定义了4个事务隔离级别,对事务处理性能的影响也个不相同。这些隔离级别是考虑了事务并发执行必须避免的3个现象提出的。 3个应该避免的现象为: u 脏读:一个事务可以读取其他事务写入但还没有提交的数据。 u 不可重复读(模糊读):一个事务重复读到以前读到的和查询到的数据,这些数据是其他的已提交事务已经修改或者删除的数据。 u 幻影读:一个事务重复运行查询返回的一些列行,这些行包括其他已经提交的事务已经插入的额外的行。 SQL92根据这些对象定义了4个隔离级别,事务运行在特定的隔离级别允许特别的一些表现。如表13-1所示。 表13-1 隔离级别阻止的读现象
Oracle提供提交读(read commited)和串行性(serializable)隔离级别,而只读模式不是SQL92的一部分。提交读是默认的。 锁机制概述 通常来说,多用户数据库使用多种类型的数据锁来解决相关数据并发、一致性和完整性的问题。锁是防止访问同一资源的事务的破坏性干扰的一种机制。 资源包括两种通用的类型: u 用户对象,例如表、行(结构和数据) u 用户不可见的系统对象,比如内存中的共享数据结构和数据字典行 Oracle如何管理数据并发性和一致性 Oracle在一个多用户环境中维护数据一致性,是通过使用多版本一致性模型和不同类型的锁和事务来做到的。这一部分包含下列主题: u 多版本并发控制 u 语句级别读一致性 u 事务级别读一致性 u 真正应用集群的读一致性 u Oracle隔离级别 u 提交读和串行性隔离级别的比较 u 隔离级别的选项 多版本并发控制 Oracle自动为一个查询提供读一致性,就是说查询结果来源于一个单个的时间点(语句级别读一致性)。Oracle还为事务中所有的查询提供读一致性(事务级别读一致性)。 Oracle使用回滚段中维护的信息来提供这些一致性视图。回滚段包含未提交的事务或最近提交的事务修改的数据的原始值。图13-1显示了Oracle如何通过回滚段中的数据提供语句级别的读一致性。 图13-1 读一致性和事务 当一个查询进入执行阶段,就确定了当前系统修改号(SCN)。在图13-1中,系统修改号为10023。在查询读取数据块时,只有对写入SCN号可见的块才会使用。块中的修改数据(更近的SCN)从回滚段中重新构建数据,这些重构数据返回给查询。因而,每个查询返回查询开始的时间的SCN涉及的所有提交数据。在查询执行时其他事务造成的修改不被采用,确保每个查询返回的都是一致性数据。 语句级别读一致性 Oracle总是执行语句级别读一致性。这可以确保单个查询返回的所有数据来源于单个时间点(查询开始的时间点)。因而,一个查询看不到脏数据或者查询执行期间其他事务提交的任何改变。查询执行开始后,只有查询开始之前提交的数据可以被查询到。查询不能看到语句开始执行之后提交的任何数据。 Oracle自动为每个查询提供一致性结果,确保数据一致性,而不需要用户参与。包含子查询的SELECT、INSERT子句,UPDATE和DELETE所有查询数据,不管是显式的还是隐式的SQL语句都返回一致性数据。这些语句使用一个查询来确定要影响哪些数据(SELECT、INSERT、UPDATE或者DELETE)。 一个SELECT语句是一个显式的查询,可以包含嵌套查询或者关联操作符。一个INSERT语句可以使用嵌套查询。UPDATE和DELETE语句可以使用WHERE子句或者子查询来只影响表中的一些行。 使用INSERT、UPDATE或者DELETE语句确保一系列一致性的结果。但是,他们并不能看到本身DML语句所做的改变。换句话说,这些操作的查询只能看到这些操作发生改变之前的数据。 注意:如果一个SELECT列表包含一个函数,那么数据库在运行PL/SQL函数代码中SQL的语句级别提供语句级别读一致性,而不是父级别。例如,一个函数可以访问另一个用户已经修改和提交的表数据。对于函数中的每个SELECT执行,都会创建一个新的读一致性快照。 事务级别读一致性 Oracle还提供事务级别读一致性选项。当一个事务运行在串行性(serializable)摸式下,所有的数据访问反映的数据库状态是事务开始的时间。这意味着同一个事务中所有查询看到的数据都和一个时间点保持一致,当然这个事务本身所做的改变自己可以看到。事务级别的读一致性会产生重复读,但不会产生幻影读。 真正应用集群的读一致性 真正应用集群(RAC)使用cache到cache的块传输机制(cache融合)来从一个实例到另一个实例传输块的读一致映像。RAC使用高速度、低等待时间的互联方式完成传输,满足远程对数据块的请求。 Oracle隔离级别 Oracle提供了3种隔离级别:
设置隔离级别 应用程序设计人员、应用程序开发人员和数据库管理员可以根据应用程序和负载情况来为不同的事务选择合适的隔离级别。你可以在事务开始时通过使用下列语句来设置事务隔离级别。 SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; SET TRANSACTION READ ONLY; 在每个事务开始用SET TRANSACTION语句设置隔离级别,会增加网络和处理负担,也可以使用ALTER SESSION语句来为所有以后的事务设定隔离级别: ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE; ALTER SESSION SET ISOLATION_LEVEL READ COMMITTED; 提交读隔离级别 Oracle默认隔离级别是提交读。这个隔离级别对于事务很少冲突的环境是合适的。Oracle让每个查询根据它自己的实体化视图时间运行,因而允许不可重复读和多次执行一个查询导致的幻影读,但会提供很高的性能。提交读隔离级别是事务很少冲突的环境的合适的隔离级别。 串行性隔离级别 串行性隔离级别适合如下环境: u 大数据库中存在影响少量行的小事务的情况下 u 两个并行事务很少修改同一行的情况下 u 相对较长时间运行的事务主要是只读的 串行性隔离级别允许并行事务只能修改那些他们可以修改的部分,就如同事务被调度依次运行一样。明确来说,Oracle只有确定串行性事务开始时,其他事务修改一个行的修改已经提交,才会允许在这个数据行上使用串行性事务。 为加快这个规则的效率,Oracle在数据块上保留控制信息,显示了块中哪些行包含提交的和未提交的数据。某种程度上来说,块包含最近影响块上每一行的事务的历史。可以保留的历史由CREATE TABLE和ALTER TABLE语句的INITRANS参数来控制。 在某些情况下,Oracle没有足够的历史信息来确定是否一行被最近的事务修改过。当很多事务并发的修改同一个数据块或者在很短的时间发生会出现这种情况(没有足够的空间记录历史)。如果一个表经常被很多事务修改同一个块,你可以通过对表的INITRANS设置很高的值来避免这种情况(没有足够的空间记录历史)。这使得Oracle在每个块中分配足够的空间来记录最近访问块的事务的历史。 如果串行性事务试图修改和删除其他在本事务开始之后的其他事务提交的数据,会产生一个错误: ORA-08177: Cannot serialize access for this transaction 当串行性事务因为Cannot serialize access错误而失败时,应用程序可以采取下列措施: u 提交这个时间点执行的工作 u 执行另外(但是不同)的语句(可能回滚到事务早期构建的保存点) u 撤销整个事务 图13-2 显示了Cannot serialize acess错误导致的失败时,一个应用程序回滚和重试事务的例子: 图13-2 串行性事务失败 提交读和串行性隔离级别的比较 Oracle为应用开发人员提供了两种不同规格的隔离级别的选项。提交读和串行性隔离级别都提供很高级别的一致性和并发性。两个级别都为降低竞争而提供了读一致性多版本并发性控制模型和行级互斥锁的实现,并且都是为真实世界的应用部署设计的。 事务系列一致性 在Oracle中查看提交读和串行性隔离级别的一个很有用的办法是考虑下列场景:假定你有一系列数据库表(或者任何系列数据),这些表的一个特定顺序的行读,在任何特定时间提交的一系列事务。一个操作(查询或者事务)如果它的所有数据读是同一系列的提交事务写入的,那么这个操作是事务系列一致性的。如果它的数据读一部分由一系列事务影响,一部分由另一系列事务影响,那么它就不是事务系列一致性的。操作是否事务系列一致性实际上看是数据库所处状态是否反映了单个系列的提交事务。 Oracle为提交读模式的事务执行的每个语句提供事务系列一致性。串行性模式岁每个事务提供事务系列一致性。 图13-2汇总了Oracle中提交读和串行性事务的主要不同。 图 13-2 提交读和串行性事务
行级别锁 提交读和串行性事务都使用行级锁,都会在试图修改一个没有提交的并行事务更新的行时产生等待。第二个事务事务等待其他事务提交或者撤销来释放它的锁,才能更新给定的行。如果其他事务回滚了,不管等待的事务是什么隔离级别,都可以对以前锁定的行进行修改,就好像其他事务不存在一样。 但是,如果其他事务提交并释放了它的锁,提交读事务可以执行它想要的修改。一个串行性事务会产生一个Cannot serialize access错误而失败,因为其他事务提交的修改是串行性事务开始之后的事情了。 引用完整性 因为Oracle在读一致性或串行性事务不使用读锁,一个事务读取的数据可以被另外一个覆盖。在应用级别执行数据完整性检查的事务不能假设他们读取的数据在事务执行期间保持不变,即使这些改变对事务不可见也不行。即使在串行性事务的情况下,如果应用级别的一致性检查如果不用心编写,也会导致数据库不一致。 注意:你可以在真正应用集群中使用提交读和串行性事务隔离级别。 分布式事务 在分布式数据库环境中,给定事务在多个物理数据库中更新数据的通过两步提交确保所有的节点都提交或者都没有提交。在这样的环境中,参加串行性事务的所有的服务器,不论是Oracle还是非Oracle,都要求支持串行性模式。 如果串行性事务试图在一个不支持串行性事务的服务器的数据库的数据,事务会返回一个错误。事务会撤销,只有在远程服务器支持串行性事务时才进行新的尝试。 相比之下,读一致性事务可以在不支持串行性事务的服务器之间执行分布式事务。 隔离级别选项 应用程序设计人员和开发人员需要依据应用程序性能、一致性需求和应用程序编码需求而选择一个隔离级别。 在多并发用户快速提交事务的环境中,设计人员必须基于期望的事务发生率和需要的回应时间来评估事务性能。对于大多数高性能环境来说,隔离级别的选择会在并发性和一致性之间做一个平衡。 检查数据库一致性的应用程序逻辑必须考虑一个事实:任何模式下读不阻塞写。 Oracle隔离级别通过行级别锁和Oracle多版本并发控制系统提供了高水平的一致性、并发性和性能。Oracle中读写互相不阻塞。因而,虽然查询仍然看到一致性数据,不需要读取未提交数据,但是提交读和串行性隔离提供了高水平的高性能的并发。 Oracle提交读隔离级别为每个查询提供事务系列一致性。就是说,每个查询看到的数据是一致性状态的。因而,读一致性隔离级别可以满足大部分应用程序,如果在其他没有使用多版本并发控制的数据库管理系统上,类似程序可能需要更高的隔离级别。 提交读隔离模式并不会让应用程序陷入Cannot serialize access错误并循环重新开始一个事务。在大多数应用程序中,事务很少需要重复发布同一个查询,所以对于大部分应用程序来说,避免幻影读和不可重复读并不重要。因而很多开发人员选择提交读模式来避免在每个事务中编写这样的错误检查和重试代码。 串行性隔离级别 Oracle串行性隔离级别适合于两个并发事务很少修改同一行和长时间运行的事务主要是只读的环境。它还适合大型数据库且只更新少量行的短事务的环境。 串行性隔离模式某种程度上通过避免不可重复读和幻影读来提供了更多的一致性,如果在一个读写事务中存在一个执行多次的查询,串行性隔离模式非常重要。 和其他串行性隔离的实现不同,这些实现将读和写一样锁定。Oracle提供了非锁定查询和行级别锁的良好的颗粒度,上述两点降低了读写的争用。对于发生大量读写争用的应用来说,Oracle串行性隔离比其他系统可以提供更高的吞吐量。因而,适合Oracle串行性的应用程序未必适合其他系统。 Oracle串行性事务的所有查询将数据库整个作为一个时间点,所以这个隔离级别适合在读/写事务中发布多个一致性查询。一个包含写入功能的报表应用程序指的是汇总数据并保存在数据库中,这样的程序可以使用串行性模式,因为它提供了READ ONLY事务提供的一致性,还允许INSERT、DELETE和UPDATE。 注意:事务中的DML语句包含子查询应该使用串行性隔离来确保一致性读。 编写串行性事务需要额外的工作,应用程序开发人员需要检查Cannot serialize acess错误并且撤销或重试事务。类似的额外代码在其他数据库管理系统中是用来管理死锁的。为适应公用标准或在多个数据库管理系统中运行的应用程序,可能需要为串行模式事务单独设计。检查串行性失败和重启的事务应该使用提交读模式,这样就不会产生串行性错误。 串行性模式在相对较长事务更新的行和大量短更新事务访问的行一样时并不适合。因为长时间运行的事务未必是给定行的第一个更新,它需要重复回滚,浪费一些工作。注意,串行性模式的通常的读锁、悲观锁实在这种环境中也不适合,因为长时间运行的事务,即使是读锁,也会阻塞短更新事务的进度,反过来也是如此。 在使用串行性模式时,应用开发人员需要考虑事务回滚和重试的开销。在读锁环境中,死锁经常发生,使用串行性模式需要通过结束事务来回滚工作,然后重试。在一个高争用的环境中,这种操作会占用大量资源。 在大部分环境中,一个接收到Cannot serialize access错误而重启的事务未必会遇到另一个冲突的事务。出于这个原因,这些语句在串行性事务中尽早和其他事务竞争会比较有帮助。但是,并不能保证事务可以成功完成,所以应用程序应该编码来限制重试的次数。 虽然Oracle串行性模式是兼容SQL92,并且和读锁实现比较还提供很多优势,但是它不能对那样的系统提供语法上的标识。应用设计人员必须考虑这样的事实:Oracle并不像其他系统一样读阻塞写。在应用程序级别检查数据库一致性的事务需要使用代码:SELECT FOR UPDATE。在其他使用串行性模式的事务的环境移植到Oracle可以考虑使用这个语句。 静默数据库 你可以将系统设置为静默数据库。处于静默状态的系统没有活动的会话,除非是用户SYS和SYSTEM。一个活动事务是指会话中包含一个事务、一个查询、一个数据获取或者一个PL/SQL过程,或者当前持有任何共享资源的会话(例如:队列共享着一些串行访问数据库资源的内存结构,且和一个会话或事务关联)。数据库管理员是仅有的可以在系统处于静默状态还可以操作的用户。 数据库管理员可以在静默状态执行一个特定的操作,这些操作在非静默状态可能是不安全的。这些动作包括: u 如果存在并发用户事务或查询可能失败的动作。例如,如果一个并发事务访问的表的模式无法修改 u 动作的中间结果对于用户并发事务或查询可能有害。例如,假设有一个大表T和一个PL/SQL包在上面操作。你可以将表T分成两个表T1和T2,修改PL/SQL包来引用信标T1和T2,而不是旧表T。 当数据库处于静默状态,你可以这样做: CREATE TABLE T1 AS SELECT ... FROM T; CREATE TABLE T2 AS SELECT ... FROM T; DROP TABLE T; 然后你可以删除旧的PL/SQL包,重新创建它。 对哪些必须持续工作的系统来说,不需要关闭数据库就能够执行这样动作的能力是很重要的。 数据库资源管理器在系统静默状态时阻塞了除SYS和SYSTEM的所有新用户。这些动作在系统返回正常状态(非静默)时可以执行。用户不会从静默状态获得任何额外的错误信息。 数据库如果设定为静默 数据库管理员使用ALTER SYSTEM QUIESCE RESTRICTED语句来静默数据库。只有用户SYS和SYSTEM才能发布ALTER SYSTEM QUIESCE RESTRICTED语句。对于数据库开放状态的所有实例,发布这个语句有如下影响: u Oracle指示所有实例中的数据库资源管理器阻止所有除SYS和SYSTEM用户之外的所有非活动会话变为活动会话。除SYS和SYSTEM之外的用户无法启动一个新会话、新事务、新查询、新的数据获取或者新的PL/SQL操作。 u Oracle等待所有实例中的非SYS或SYSTEM的用户所有现存事务完成(提交或者结束)。Oracle还等待所有非SYS或SYSTEM的用户运行的不在事务中的查询、数据获取和PL/SQL过程完成。如果查询由多个连续的OCI获取执行,Oracle并不等待所有的获取结束。它等待当前的获取结束,阻塞新的数据获取。Oracle还等待所有持有任何共享资源(比如队列)的所有会话(非SYS或者SYSTEM)释放这些资源。这些操作完成之后,Oracle将数据库置入静默状态,完成QUIESCE RESTRICTED语句的执行。 u 如果实例运行在共享服务器模式,Oracle指示数据库资源管理器来阻塞这个实例上的登陆(非SYS和SYSTEM)。如果一个实例运行在非共享服务器模式,Oracle并不对实例的登陆施加任何限制。 在静默状态期间,你不能修改任何实例的资源管理计划。 ALTER SYSTEM UNQUIESCE语句将所有运行实例返回到正常状态,所以所有阻塞动作可以继续处理。一个管理员可以通过查询v$blocking_quiesce视图来确定锁定的会话。 Oracle如何锁定数据 锁是一种阻止访问同一资源的事务破坏性干扰的一种机制,资源可以是用户对象(如表和行)或用户不可见的系统对象(比如内存中的共享数据结构和数据字典行)。 在所有的情况下,Oracle都是在SQL语句执行时自动获得需要的锁,所以用户不需要关心这些细节。Oracle自动使用最低的、适当的限制来提供高水平的数据并发,还提供数据完整性防护。Oracle还允许用户手动锁定数据。 事务和数据并发 Oracle在事务之间使用锁机制来提供数据并发性和完整性。因为Oracle的锁机制和事务控制紧密结合,应用设计人员只需要适当定义事务,Oracle自动管理锁。 记住Oracle锁是完全自动的,不需要任何用户干预。所有SQL语句执行时隐式加锁,所以数据库用户不需要显式锁定任何资源。Oracle默认的锁机制在数据上提供低级别的限制来确保数据完整性,还允许高水平的数据并发性。 锁模式 Oracle在多用户数据库中使用两种模式的锁: u 排他锁:阻止相关资源被共享。修改数据自动获得这个锁模式。如果排他锁没有释放,排他锁定一个资源的第一个事务是仅有的一个可以修改该资源的事务。 u 共享锁:允许相关资源被共享(和涉及的操作有关)。多个用户读取数据可以共享数据、持有共享锁来阻止并发的写入权限(需要排他锁)。多个事务可以在同一个资源上获得共享锁。 锁持续期 事务内语句获得的所有锁在事务存续期间持有,阻止并发事务的脏读、丢失更新和破坏性的DDL操作之类的破坏性干扰。一个事务的SQL语句造成的改变,只有提交之后其他的事务才能看到。 在事务提交或者撤销时,Oracle会释放事务中的语句获得的锁。回滚到保存点时,Oracle也释放保存点后的锁。但是,只有事务不再等待以前锁定的资源时,才可以在现在可用的资源上获得锁。等待事务一直等待,除非原始事务完全提交或者回滚之后。 数据锁转换和锁升级 一个事务持有事务内插入、更新、删除行的排他锁。因为行锁是在最高级别的限制上获得,不需要执行锁转换。 Oracle根据需要自动将低限制的表锁转换为一个高限制的锁。例如,假定一个事务使用了包含FOR UPDATE子句的SELECT语句来锁定了表的行。因而,它获得了表的排他行锁和行共享表锁。如果事务后来更新了一个或多个锁行,共享表锁的行自动转换为一个排他表锁的行。 在大量同一级别的颗粒(如行)拥有锁时,一个数据库将锁升级为高水平的颗粒(如表),这个叫做锁升级。例如,如果一个用户锁定了一个表很多行,某些数据库自动逐步将用户行锁升级到单个表。锁的数量减少了,但是锁定的限制行增加了。 Oracle不会升级锁。升级锁大大的增加了死锁的可能性。想象一种情况,系统试图在事务T1中升级锁,但是因为事务T2持有锁,而无法获得。如果事务T2也对同样的数据需要升级锁,就会发生死锁。 死锁 在两个或者更多的客户等待彼此的数据锁时就会发生死锁。死锁阻止了某些事务继续工作。图13-3是一个死锁中两个事务的假想说明。 在图 13-3中,A点没有任何问题,因为每个事务都持有它视图更新的行的锁。每个事务不需要中断就可以处理。但是,每个事务都视图更新当前被其他事务持有的行。因而,死锁在B点发生,因为没有事务可以获得它执行或者结束的需要的资源。这是一个死锁,因为无论每个事务等待多久,冲突锁还是不释放。 图13-3 死锁中两个事务 死锁检测 Oracle自动检测死锁情况,并通过回滚死锁中包含的一个语句来解决死锁,从而释放一系列冲突的行锁。遭受语句级别回滚的事务会返回对应的消息。回滚的语句属于检测到死锁的事务。通常,被告知的事务应该显式回滚,但也可以等待之后重试回滚语句。 注意:在分布式事务中,本地死锁通过分析等待数据检测,全局死锁通过超时来检测。检测之后,分布式死锁和非分布式死锁的数据库和应用处理方式相同。 死锁通常是事务显式的覆盖Oracle的默认锁时发生。因为Oracle本身没有锁升级,查询也不使用读锁,只使用行锁(而不是页锁),在Oracle中很少发生死锁。 避免死锁 如果访问同样表的事务按照同样的顺序锁定这些表,不管通过显式的还是隐式的,多数死锁都可以避免的。例如,所有的应用开发人员可以遵循下列规则:主表和子表都需要更新时,先锁定主表,再锁定子表。如果这类规则严格指定,所有应用程序都遵守,死锁就很少会发生。 当你要为一个事务请求一系列锁,考虑首先获得最排他的锁(兼容性最好)。 锁类型 Oracle自动使用不同类型的锁来控制数据的并发访问,阻止用户之间的破坏性干扰。Oracle自动为事务锁定一个资源,阻止其他需要同一资源的共享锁的事务获得锁。在某些条件下,事务不再需要资源时,Oracle自动释放锁。 在这个操作中,Oracle自动根据资源锁定情况和执行的操作在不同的限制级别上获得不同类型的锁。 Oracle锁通常分为3类。
下面开始讨论DML锁、DDL锁和内部锁。 DML锁 DML锁(数据锁)的目的是确保多个用户并发访问的数据的完整性。DML锁阻止了同时发生的冲突的DML或者DDL操作造成的破坏性干扰。DML语句自动获得行级锁和表级锁。 注意:每类锁或锁类型后面括号里的缩写词是在企业管理器的锁监视器使用的缩略词。企业管理器可能用TM显示任何表锁,而不是显示表锁的类型(如RS或SRX)。 行锁(TX) 行级锁主要用于阻止两个事务同时修改一行。当事务需要修改一行时,需要获得一个行级锁。 一个语句或者事务可以持有行锁的数目并没有限制,Oracle并不会将行级锁升级为粗粒度的锁。行锁提供了最好的颗粒度和最好的并发性和吞吐量。 多版本并发控制和行级别锁定结合意味着只有用户访问同一行的数据时才会产生竞争,具体如下: u 读数据不需要等待同一行的写入 u 写入数据不需要等待同一行数据的读取,除非使用了SELECT .... FOR UPDATE语句,这个语句明确的请求了读锁。 u 写入只在其他写入试图同时更新同一行数据时才会等待 注意:读数据可能在某些特殊情况下(分布式事务中)等待同一数据块的写入。 一个事务通过下列语句修改的每行都会获得一个行级互斥锁:INSERT、UPDATE、DELETE和SELECT ... FOR UPDATE。 行修改总是排他锁,所以其他的事务无法修改这一行,除非持有锁的事务提交或者回滚了。但是,如果事务由于实例失败而死掉,块级别的恢复会在整个事务恢复之前使行可用。Oracle上述语句的情况下自动获得行级锁。 如果一个事务在一行上获得一个行锁,事务也在对应表上获得一个表锁。这个表锁阻止了冲突的DDL操作,这个DDL操作可能覆盖当前事务中的数据修改。 表锁(TM) 表级锁主要用于并发控制并发的DDL操作,比如阻止在一个DML操作过程中删除相关表。当在一个表上执行DDL或DML语句,就获得一个表锁。表锁不影响DML操作的并行性。对于分区表,表锁在表和子分区级别上都可以获得。 一个事务在一个表使用下列DML语句:INSERT、UPDATE、DELETE、SELECT....FOR UPDATE和LOCK TABLE时获得一个表锁。这些DML操作获得表锁有两个目的:为事务保留表的DML访问权限和阻止和这个事务冲突的DDL操作。任何表锁都阻止了同一个表上的排他DDL锁的获得,从而阻止了需要这个锁的DDL操作。例如,如果一个没有提交的事务持有一个表的表锁,表不能修改结构和删除。 表锁可以以多种方式持有:行共享(row share,RS)、行排他(row exclusive,RX)、共享行排他(share row exclusive,SRX)、和排他锁(exclusive,X)。一个表的锁模式的限制确定了同一表上可以获得和持有的其他表锁。 表 13-3显示了语句获得表锁模式和这表锁允许和禁止的操作。 表 13-3 表锁的汇总
RS:行共享锁 RX:行排他锁 S:共享锁 SRX:共享行排他锁 X:排他锁 *:如果没有其他事务持有冲突的行锁就为是,否则发生等待。 下面开始说明表锁的每个模式,从最少限制到最多限制。还描述了在那个模式下导致事务获得表锁的动作和这种锁模式下其他事务允许和禁止的动作。 行级共享表锁(RS) 行级共享表锁(有时候也叫部分共享表锁)指出持有表上锁的事务已经在表上锁定了行,要更新他们。一个行级表共享锁在下列SQL语句运行时自动获得: SELECT .... FROM table .... FOR UPDATE OF .....; LOCK TABLE table IN ROW SHARE MODE; 行级共享表锁是最少限制的表锁,为表提供最高水平的并发性。 允许的操作:持有行共享表锁的事务允许其他事务并发的在同一表上执行查询、插入、更新、删除或者锁定行。因而,其他事务可以在同一个表上同时获得行共享、行排他、共享、共享行排他表锁。 禁止的操作:持有行共享表锁的事务阻止其他事务使用下面的语句使用排他写访问同一个表: LOCK TABLE table IN EXCLUSIVE MODE; 行排他表锁(RX) 一个行排他表锁(也叫做部分排他表锁,SX)通常显示持有锁的事务已经在表上更新了一行或多行。一个行排他表锁在使用下列语句修改表时自动获得: INSERT INTO table ....; UPDATE table ...; DELETE FROM table ....; LOCK TABLE table in ROW EXCLUSIVE MODE; 行排他表锁比行共享表锁的限制多一些。 允许的操作:一个事务持有的行排他表锁允许其他事务在同一个表上并发的查询、插入、更新、删除或锁定行。因而,行排他表锁允许多个事务在一个表上同时拥有表排他锁和表共享锁。 禁止的操作:一个事务持有的行排他表锁阻止其他事务手工锁定表来执行读取或写入。因而,其他事务不能使用下面的语句并发的锁定表: LOCK TABLE table IN SHARE MODE; LOCK TABLE table IN SHARE EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; 共享表锁(S) 对表执行下面的语句会自动或共享表锁: LOCK TABLE table IN SHARE MODE; 允许的操作:一个事务持有的共享表锁允许其他事务只能查询表、使用SELECT .... FOR UPDATE来锁定特定行或者运行LOCK TABLE ... IN SHARE MODE语句。不允许其他事务进行任何更新。多个事务可以并发的持有同一个表的共享表锁。在这种情况下,没有事务可以更新表(即使一个事务使用SELECT ... FOR UPDATE语句持有行锁也不行)。因而,一个持有共享表锁的事务只有在没有其他事务持有这个表的共享表锁的情况下更新表。 禁止的操作:一个事务持有的共享表锁阻止其他事务修改这个表和阻止执行下面的语句: LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; LOCK TABLE table IN ROW EXCLUSIVE MODE; 共享行排他表锁(SRX) 共享行排他表锁(也叫共享部分排他表锁,SSX)比共享锁限制多一些。使用下面的语句可以获得一个共享行排他表锁: LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; 允许的操作:给定一个表同时只有一个事务可以获得共享行排他表锁。一个事务持有的共享行排他表锁允许其他事务查询或者使用SELECT ... FOR UPDATE语句来锁定特定行,但不允许更新表。 禁止的操作:一个事务持有的共享行排他表锁阻止其他事务获得行排他表锁和修改这个表。还阻止其他事务获得共享锁、共享行排他锁、排他表锁,这些阻止其他事务执行下列语句: LOCK TABLE table IN SHARE MODE; LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE table IN ROW EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; 排他表锁(X) 排他表锁是限制最多的表锁,允许持有锁的事务排他的写入到表中。一个排他表锁使用下列语句可以获得: LOCK TABLE table IN EXCLUSIVE MODE; 允许的操作:只有一个事务可以获得表的排他表锁,一个排他表锁只允许其他事务查询表。 禁止的操作:一个事务持有的排他表锁禁止其他事务执行任何类型的DML操作或者阻止在表上放置任何类型的锁。 DML语句自动获得的锁 前面的部分描述了不同类型的数据锁、可以获得那种锁类型、什么时候可以获得锁、什么时候获得锁和禁止什么操作。下面汇总了Oracle如何为不同的DML操作自动锁定数据。 在下面的部分,表 13-4汇总了信息。 表13-4 DML语句获得的锁
|