Oracle 20C官方文档-Concepts(Part III-1)
Oracle® Database
Database Concepts
Part I Oracle Relational Data Structures
Part II Oracle Data Access
Part III Oracle Transaction Management
---1 Data Concurrency and Consistency
---2 Transactions
Part IV Oracle Database Storage Structures
Part V Oracle Instance Architecture

数据并发性和一致性
9 Data Concurrency and Consistency
本章解释了Oracle数据库如何在多用户数据库环境中维护一致的数据。
本章包括以下章节:
1 数据并发性和一致性介绍
2 Oracle数据库事务隔离级别概述
3 Oracle数据库锁定机制概述
4 自动锁概述
5 手动数据锁概述
6 用户定义锁的概述
1数据并发性和一致性介绍
在单用户数据库中,用户可以修改数据,而不必担心其他用户同时修改相同的数据。但是,在多用户数据库中,多个并发事务中的语句可以更新相同的数据。同时执行的事务必须产生有意义和一致的结果。
多用户数据库必须提供以下内容:
•保证用户可以同时访问数据(数据并发性)
•确保每个用户看到的数据视图一致(数据一致性),包括用户自己的事务和其他用户提交的事务所做的可见更改。
为了描述事务并发运行时的一致事务行为,数据库研究人员定义了一个称为序列化的事务隔离模型。序列化事务在一个环境中运行,该环境使它看起来好像没有其他用户在修改数据库中的数据。
虽然事务之间的这种隔离程度通常是可取的,但是以可串行化模式运行许多应用程序会严重影响应用程序的吞吐量。并发运行的事务的完全隔离可能意味着一个事务不能执行对另一个事务正在查询的表的插入。简而言之,现实世界的考虑通常需要在完美的事务隔离和性能之间做出妥协。
Oracle数据库使用多版本一致性模型和各种类型的锁和事务来维护数据一致性。通过这种方式,数据库可以将数据视图呈现给多个并发用户,每个视图在某个时间点上保持一致。因为可以同时存在不同版本的数据块,所以事务可以读取在查询所需的时间点提交的数据版本,并返回与单个时间点一致的结果。
多版本读一致性
在Oracle数据库中,多版本控制是同时实现多个数据版本的能力。Oracle数据库维护多版本读取一致性。
Oracle数据库查询具有以下特点:
•Read-consistent查询
查询返回的数据在某个时间点被提交并保持一致。
为了说明脏读的问题,假设一个事务更新了一个列值而没有提交。第二个事务读取更新的、未提交的值。第一个会话回滚事务,使列具有原来的值,而第二个事务使用更新后的值继续处理,从而破坏数据库。脏读会破坏数据完整性,违反外键,并忽略唯一的约束。
•非阻塞查询
数据的读和写不会相互阻塞。
语句级别读一致性
Oracle数据库总是强制执行语句级别的读一致性,它保证单个查询返回的数据在单个时间点上是提交的和一致的。
单个SQL语句一致的时间点取决于事务隔离级别和查询的性质:
•在read committed隔离级别,这一点是打开语句的时间。例如,如果SELECT语句在scn1000处打开,那么该语句与scn1000一致。
•在可序列化或只读事务中,此点是事务开始的时间。例如,如果一个事务从scn1000开始,并且在这个事务中出现多个SELECT语句,那么每个语句都与scn1000一致。
•在闪回查询操作中(SELECT ... AS OF),SELECT语句显式地指定时间点。例如,您可以查询上周四下午两点表的数据。
事务级读一致性
Oracle数据库还可以为事务中的所有查询提供读一致性,即事务级的读一致性。
在本例中,事务中的每个语句都看到来自同一时间点的数据。这是交易开始的时间。
由可序列化事务进行的查询将看到事务本身所做的更改。例如,更新雇员然后查询雇员的事务将看到更新。事务级别的读取一致性产生可重复的读取,并且不会将查询公开给幻像读取。
读取一致性和UNDO段
为了管理多版本读取一致性模型,数据库必须在同时查询和更新表时创建一组读取一致性的数据。
Oracle数据库通过undo数据实现读一致性。
每当用户修改数据时,Oracle数据库都会创建undo条目,并将其写入undo段。撤消段包含未提交或最近提交的事务更改过的数据的旧值。因此,同一数据在不同时间点的多个版本可以存在于数据库中。数据库可以使用不同时间点的数据快照来提供数据的读一致视图,并支持非阻塞查询。
在单实例和Oracle RAC环境中保证了读一致性。Oracle RAC使用一种称为缓存融合的缓存到缓存的块传输机制,将数据块的读一致性镜像从一个数据库实例传输到另一个数据库实例。
读一致性:示例
这个示例显示了一个查询,该查询使用undo数据在read committed隔离级别提供语句级别的读一致性。

当数据库查询检索数据块时,数据库确保每个块中的数据反映了查询开始时块的内容。数据库根据需要回滚对块的更改,以便将块重新构造到查询开始处理的时间点。数据库使用一种称为SCN的内部排序机制来保证事务的顺序。当SELECT语句进入执行阶段时,数据库确定在查询开始执行时记录的SCN。在图9-1中,这个SCN是10023。查询只看到关于SCN 10023的提交数据。
在图9-1中,10023之后带有SCN的块表示数据发生了变化,如带有SCN 10024的两个块所示。SELECT语句需要与提交的更改一致的块版本。数据库将当前的数据块复制到一个新的缓冲区中,并应用undo数据来重构这些块以前的版本。这些重建的数据块被称为一致性读(CR)克隆。
在图9-1中,数据库创建了两个CR克隆:一个与SCN 10006一致,另一个与SCN 10021一致。数据库返回查询的重建数据。通过这种方式,Oracle数据库可以防止脏读。
读取一致性和相关事务列表
每个段块的块头包含一个相关的事务列表(ITL)。
当数据库开始修改块时,数据库使用ITL来确定事务是否未提交。
ITL中的条目描述哪些事务的行已锁定,以及块中的哪些行包含已提交和未提交的更改。ITL指向undo段中的事务表,该表提供关于对数据库进行更改的时间的信息。
在某种意义上,块头包含影响块中每一行的事务的最新历史记录。CREATE TABLE和ALTER TABLE语句的INITRANS参数控制保存的事务历史记录的数量。
读取一致性和延迟插入
一种称为延迟插入的特殊插入类型不使用标准的读一致性机制。
延迟插入使用MEMOPTIMIZE_WRITE提示插入到指定为memoptimization FOR WRITE的表中。数据库将这些插入缓冲在大池中,而不是buffer cache中。数据库不使用redo和undo跟踪更改。相反,当空间管理协调器(SMCO)将缓冲区写入磁盘时,数据库将自动提交更改。不能回滚更改。
延迟插入与常规插入有以下重要区别:
•驻留在应用程序假定已提交的大池中的数据可能会丢失。例如,在将更改保存到磁盘之前,数据库实例可能会失败,即使应用程序报告更改已保存。
•不允许直接从内存中读取数据。在后台进程将更改写入到磁盘之前,写入器无法读取自己的更改。在提交的更改被写到磁盘之前,读取器无法看到它们。
必须避免数据丢失的客户机应用程序应该在将数据写入大池后保留数据的本地副本。客户端可以使用dbms_memoptimization包来跟踪写入内存的持久性,使用DBMS_MEMOPTIMIZE_ADMIN包来强制数据库写入磁盘。
锁机制
通常,多用户数据库使用某种形式的数据锁定来解决与数据并发性、一致性和完整性相关的问题。
锁是一种防止访问同一资源的事务之间发生破坏交互的机制。
ANSI/ISO事务隔离级别
ANSI和ISO/IEC都采用了SQL标准,它定义了四种事务隔离级别。这些级别对事务处理吞吐量有不同程度的影响。
这些隔离级别是根据在并发执行的事务之间必须防止的现象定义的。可预防的现象有:
•脏读
事务读取了另一个尚未提交的事务的数据。
•不可重复(模糊)读
事务重新读取以前读取的数据,发现另一个提交的事务已修改或删除该数据。例如,用户查询一行,然后再查询同一行,结果发现数据已更改。
•幻读
事务重新运行返回一组满足搜索条件的行的查询,并发现另一个提交的事务已插入满足该条件的其他行。
例如,事务查询员工数。五分钟后,它执行相同的查询,但现在这个数字已经增加了一个,因为另一个用户插入了新员工的记录。与以前相比,满足查询条件的数据更多,但与模糊读取不同,以前读取的数据没有变化。
SQL标准根据允许在特定隔离级别上运行的事务经历的现象定义了四个隔离级别。表9-1显示了这些内容。
表9-1隔离级别可预防的读现象

Oracle数据库提供了read committed(默认)和serializable隔离级别。此外,数据库提供只读模式。
2 Oracle数据库事务隔离级别概述
事务隔离级别的ANSI标准是根据每个隔离级别允许或禁止的现象来定义的。
Oracle数据库提供事务隔离级别:
•读取提交的隔离级别 Read Committed Isolation Level
•可序列化的隔离级别 Serializable Isolation Level
•只读隔离级别 Read-Only Isolation Level
Read Committed隔离级别
在read committed隔离级别,事务执行的每个查询只看到查询之前提交的数据,而不是事务开始之前提交的数据。
这个隔离级别是默认的。它适用于事务很少可能发生冲突的数据库环境。
在已提交的事务中执行查询可以避免在查询过程中读取已提交的数据。例如,如果一个查询正在扫描一个百万行表时,并且另一个不同的事务将更新提交到第95万行,那么该查询在读取第95万行时不会看到这种变化。但是,由于数据库不阻止其他事务修改查询读取的数据,所以其他事务可能会在查询执行期间更改数据。因此,两次运行相同查询的事务可能会出现模糊读取和幻象。
读取提交隔离级别的一致性
数据库为每个查询提供一致的结果集,保证了数据的一致性,不需要用户执行任何操作。
隐式查询(如UPDATE语句中的WHERE子句所暗示的查询)保证得到一致的结果集。但是,隐式查询中的每条语句都不会看到DML语句本身所做的更改,而是看到更改之前存在的数据。
如果一个SELECT列表包含一个PL/SQL函数,那么数据库将在PL/SQL函数代码中SQL运行的语句级别上应用语句级别的读一致性,而不是在父SQL级别。例如,一个函数可以访问另一个用户更改和提交数据的表。对于函数中SELECT的每次执行,都会建立一个新的读取一致性快照。
已提交的读事务中的写冲突
在已提交的读事务中,当事务试图更改由未提交的并发事务更新的行时,会发生写冲突。
阻止行修改的事务有时称为阻塞事务。读提交事务等待阻塞事务结束并释放其行锁。
备选方案如下:
•如果阻塞事务回滚,则等待事务将继续更改之前锁定的行,就好像其他事务从未存在过一样。
•如果阻塞事务提交并释放它的锁,那么等待的事务将继续对新更改的行进行预期的更新。
下表显示了事务1(可以是可序列化的,也可以是已读提交的)如何与已读提交的事务2交互。它显示了一个被称为丢失更新的经典情况。事务1所做的更新并不在表中,即使事务1提交了更新。设计处理丢失更新的策略是应用程序开发的一个重要部分。
表9-2 READ COMMITTED事务中的冲突写入和丢失更新

表9-2(续)READ COMMITTED事务事务中,冲突的写和丢失的更新

可序列化的隔离级别
在可序列化隔离级别中,事务只看到在事务开始时提交的更改,而不是查询开始时提交的更改,以及事务本身所做的更改。
可序列化事务在一个环境中操作,使其看起来好像没有其他用户在修改数据库中的数据。可序列化隔离适用于以下环境:
•大型数据库和只更新几行的短事务
•两个并发事务修改同一行的可能性相对较低
•相对长时间运行的事务主要是只读的
在可序列化的隔离中,通常在语句级别获得的读取一致性扩展到整个事务。事务读取的任何行在重新读取时都保证相同。任何查询都保证在事务期间返回相同的结果,因此其他事务所做的更改对查询都不可见,无论它运行了多长时间。可序列化事务不会经历脏读、模糊读或幻象读。
只有当可序列化事务开始时已提交其他事务对行所做的更改时,Oracle数据库才允许可序列化事务修改行。当可序列化事务尝试更新或删除由可序列化事务开始后提交的其他事务更改的数据时,数据库将生成错误:
ORA-08177:无法序列化此事务的访问
当可序列化事务因ORA-08177错误而失败时,应用程序可以执行以下操作:
•将已执行的工作落实到这一点
•执行附加(但不同)语句,可能在回滚到事务中先前建立的保存点之后
•回滚整个事务
下表显示可序列化事务如何与其他事务交互。如果可序列化事务在可序列化事务开始后未尝试更改另一个事务提交的行,则可避免序列化访问问题。
Table 9-3Serializable Transaction

Table 9-3(Cont.) Serializable Transaction

Table 9-3(Cont.) Serializable Transaction


只读的隔离级别
只读隔离级别类似于serializable隔离级别,但是只读事务不允许在事务中修改数据,除非用户是SYS。
只读事务不容易受到ORA-08177错误的影响。只读事务对于生成报告非常有用,其中的内容必须与事务开始的时间一致。
Oracle数据库通过根据需要从undo段重新构造数据来实现读一致性。因为撤消段是以循环方式使用的,所以数据库可以覆盖撤消数据。长时间运行的报告存在这样的风险:读取一致性所需的撤消数据可能被不同的事务重用,从而引发快照太旧的错误。设置撤销保留期(undo retention period),这是数据库在覆盖旧撤消数据之前尝试保留旧撤消数据的最短时间,可以适当地避免这个问题。
3 Oracle数据库锁定机制概述
锁是一种防止破坏交互作用的机制。
当访问共享数据的事务之间不正确地更新数据或不正确地改变底层数据结构时,交互是破坏的。锁在维护数据库的并发性和一致性方面起着至关重要的作用。
锁定行为总结
根据获取锁的操作,数据库维护几种不同类型的锁。
通常,数据库使用两种类型的锁:独占锁和共享锁。在资源(如行或表)上只能获得一个独占锁,但在单个资源上可以获得多个共享锁。
锁影响读和写之间的交互。读是对资源的查询,而写是修改资源的语句。以下规则总结了Oracle数据库对读写的锁定行为:
•只有正在修改的行才会被锁定。
当语句更新一行时,事务仅获得该行的锁。通过在行级锁定表数据,数据库可以最小化对相同数据的争用。在正常情况下,数据库不会将行锁升级到块级或表级。
•一行的写入阻塞同一行的并发写入。
如果一个事务正在修改一行,那么行锁将阻止不同的事务同时修改同一行。
•读永远不会阻塞写。
因为行没有锁定该行,所以写可以修改该行。唯一的例外是SELECT ... FOR UPDATE语句,它是一种特殊类型的SELECT语句,用于锁定正在读取的行。
•写从不阻塞读。
当写入更改行时,数据库使用undo数据向读者提供一致的行视图。
在处理分布式两阶段提交时,数据库可能会在特殊情况下暂时阻止读访问。具体来说,如果在准备和提交阶段之间开始一个查询,并尝试在提交之前读取数据,那么数据库可能会将一个锁从行级提升到块级,以确保读取的一致性。
锁的使用
在单用户数据库中,不需要锁,因为只有一个用户在修改信息。但是,当多个用户访问和修改数据时,数据库必须提供一种方法来防止对相同数据的并发修改。
锁实现以下重要的数据库需求:
•一致性
在用户完成之前,其他会话不能更改会话正在查看或更改的数据。
•完整性
数据和结构必须按照正确的顺序反映对它们所做的所有更改。
Oracle数据库通过其锁机制在事务之间提供数据并发性、一致性和完整性。锁定是自动发生的,不需要用户操作。
对锁的需求可以通过对一行的并发更新来说明。在下面的示例中,一个简单的基于web的应用程序向最终用户提供了员工电子邮件和电话号码。应用程序使用如下所示的更新语句来修改数据:
UPDATE employees
SETemail = ?, phone_number = ? WHERE employee_id = ?
ANDemail = ?
ANDphone_number = ?
在前面的UPDATE语句中,WHERE子句中的电子邮件和电话号码值是指定员工的原始未修改值。此更新确保应用程序修改的行在应用程序最后一次读取并显示给用户后没有更改。通过这种方式,应用程序避免了丢失更新的问题,即一个用户覆盖另一个用户所做的更改,从而实际上丢失了第二个用户的更新(表9-2显示了丢失更新的一个示例)。
表9-4显示了两个会话试图在大致相同的时间修改employees表中的相同行时的事件序列。

Table 9-4(Cont.) Row Locking Example


Oracle数据库在执行SQL语句时自动获取必要的锁。例如,在数据库允许会话修改数据之前,会话必须首先锁定数据。锁赋予会话对数据的排他性控制,因此在锁释放之前没有其他事务可以修改锁定的数据。
由于Oracle数据库的锁定机制与事务控制紧密相关,应用程序设计人员只需正确定义事务,Oracle数据库自动管理锁定。用户不需要显式地锁定任何资源,尽管Oracle数据库也允许用户手动锁定数据。
下面几节解释一些概念,这些概念对于理解Oracle数据库如何实现数据并发非常重要。
锁模式
Oracle数据库自动使用最低适用级别的限制来提供最高程度的数据并发性,同时还提供故障安全的数据完整性。
级别的限制越少,其他用户就越容易访问数据。相反,级别越严格,其他事务所能获得的锁类型就越有限。
Oracle数据库在多用户数据库中使用两种锁定模式:
•互斥型锁模式
此模式阻止共享关联的资源。事务在修改数据时获得排他锁。第一个独占锁定资源的事务是唯一一个可以在独占锁释放之前更改资源的事务。
•共享锁定模式
此模式允许共享关联的资源,具体取决于所涉及的操作。读取数据的多个用户可以共享数据,每个用户都持有一个共享锁,以防止需要独占锁的写入器并发访问。多个事务可以获取同一资源上的共享锁。
假设事务使用SELECT … for update,以选择单个表行。事务获得排他的行锁和行共享表锁。行锁允许其他会话修改锁住的行以外的任何行,而表锁阻止会话修改表的结构。因此,数据库允许执行尽可能多的语句。
锁转换和升级
Oracle数据库根据需要执行锁转换。
在锁转换过程中,数据库会自动将限制较低的表锁转换为限制较高的表锁。例如,假设事务发出SELECT…for update用于更新雇员,然后更新锁定的行。在本例中,数据库自动将行共享表锁转换为行独占表锁。事务持有事务中插入、更新或删除的所有行的独占行锁。因为行锁是在限制程度最高的情况下获得的,所以不需要或执行锁转换。
锁转换不同于锁升级,后者发生在多个锁在一个粒度级别上(例如,行),而数据库将锁提升到更高的粒度级别(例如,表)时。如果一个会话锁住了一个表中的许多行,那么一些数据库会自动将该行锁升级为一个表锁。锁的数量减少了,但是被锁内容的限制增加了。
Oracle数据库从不升级锁。锁升级大大增加了死锁的概率。假设系统试图代表事务1升级锁,但是由于事务2持有锁而无法升级。如果事务2在继续之前还需要对相同的数据进行锁升级,则会创建死锁。
锁定时间
当某些事件发生时,Oracle数据库自动释放一个锁,以便事务不再需要该资源。
通常,数据库持有事务期间语句获取的锁。这些锁可以防止并发事务的破坏干扰,如脏读、丢失的更新和破坏的DDL。
当事务提交或回滚时,Oracle数据库释放事务中的语句获取的所有锁。Oracle数据库还释放回滚到保存点时在保存点之后获得的锁。但是,只有不等待以前锁定的资源的事务才能获得当前可用资源的锁。等待事务继续等待,直到原始事务提交或完全回滚之后。
锁和死锁
死锁是指两个或多个用户正在等待彼此锁定的数据。死锁阻止某些事务继续工作。
Oracle数据库自动检测死锁,并通过回滚死锁中涉及的一条语句来解决它们,释放一组冲突的行锁。数据库将相应的消息返回给正在进行语句级回滚的事务。回滚的语句属于检测死锁的事务。通常,发出信号的事务应该显式回滚,但它可以在等待后重试回滚语句。
表9-5说明了死锁中的两个事务。


死锁通常发生在事务显式地覆盖Oracle数据库的默认锁定时。因为Oracle数据库不升级锁,也不为查询使用读锁,而是使用行级(而不是页级)锁,所以死锁很少发生。
4 自动锁概述
Oracle数据库代表事务自动锁定资源,以防止其他事务执行需要独占访问同一资源的操作。
根据资源和正在执行的操作,数据库自动获取不同级别的锁。
Oracle数据库锁分为如下表所示的类别。

DML锁
DML锁(也称为数据锁)保证了多个用户并发访问的数据的完整性。
例如,DML锁阻止两个客户从在线书店购买书籍的最后一本。DML锁防止同时冲突的DML或DDL操作的破坏干扰。
DML语句自动获取下列类型的锁:
•行锁(TX)
•表锁(TM)
在下面的小节中,每种类型的锁或锁模式后面括号中的缩写是Oracle Enterprise Manager (Enterprise Manager)的锁监视器中使用的缩写。企业管理器可以为任何表锁显示TM,而不是指示表锁的模式(如RS或SRX)。
行锁(TX)
行锁,也称为TX锁,是表上单行的锁。事务为插入、更新、删除、合并或select ... for update语句。每一行获取一个行锁。行锁一直存在,直到事务提交或回滚。
行锁主要用作一种排队机制,以防止两个事务修改同一行。数据库总是以独占模式锁定修改后的行,以便其他事务在持有锁的事务提交或回滚之前不能修改该行。行锁定提供了尽可能好的粒度锁定,从而提供了尽可能好的并发性和吞吐量。
注意:
如果事务因数据库实例失败而终止,则块级恢复会在恢复整个事务之前使一行可用。
如果事务获取行的锁,则该事务还获取包含该行的表的锁。表锁防止发生冲突的DDL操作,这些操作将覆盖当前事务中的数据更改。图9-2说明了表中第三行的更新。Oracle数据库会自动在更新的行上放置一个独占锁,在表上放置一个子独占锁。

行锁和并发性
这个场景演示了Oracle数据库如何使用行锁实现并发。
三个会话同时查询相同的行。会话1和会话2继续对不同的行进行未提交的更新,而会话3不进行更新。每个会话可以看到自己的未提交更新,但不能看到任何其他会话的未提交更新。
Table 9-7Data Concurrency Example

Table 9-7(Cont.) Data Concurrency Example

行锁的存储
与一些使用锁管理器在内存中维护锁列表的数据库不同,Oracle数据库将锁信息存储在包含锁行的数据块中。
数据库使用排队机制来获取行锁。如果事务需要为未锁定的行设置锁,则事务将在数据块中放置锁。此事务修改的每一行都指向存储在块头中的事务ID的副本。
当一个事务结束时,事务ID保留在块标头中。如果另一个事务想要修改一行,那么它将使用事务ID来确定锁是否处于活动状态。如果锁是活动的,则会话请求在释放锁时得到通知。否则,事务将获得锁。
表锁(TM)
当表被INSERT、UPDATE、DELETE、MERGE、SELECT ...e FOR UPDATE子句或lock table语句修改时,事务将获得一个表锁,也称为TM锁。
DML操作需要表锁来代表事务保留对表的DML访问,并防止DDL操作与事务冲突。
表锁可以在下列任何一种模式下持有:
•行共享锁(RS)
这个锁,也称为子共享表锁(SS),表示持有表上锁的事务已经锁定了表中的行,并打算更新它们。行共享锁是表锁中限制最少的一种模式,它为表提供了最高程度的并发性。
•行独占表锁(RX)
这个锁,也称为subexclusive table lock (SX),通常表示持有这个锁的事务已经更新了表行或发出了SELECT…for update。SX锁允许其他事务并发地查询、插入、更新、删除或锁定同一表中的行。因此,SX锁允许多个事务同时获得同一表的SX和子共享表锁。
•共享表锁
事务持有的共享表锁允许其他事务查询该表(无需使用SELECT…for update),但仅当单个事务持有共享表锁时才允许更新。因为多个事务可能同时持有一个共享表锁,持有这个锁不足以确保一个事务可以修改表。
•共享行独占表锁(SRX)
这个锁也称为share-subexclusive table lock (SSX),比share table lock的限制更严格。一次只有一个事务可以获得给定表上的SSX锁。事务持有的SSX锁允许其他事务查询表(除了SELECT…for update)但不更新表。
•专用表锁(X)
这个锁是最严格的,禁止其他事务执行任何类型的DML语句或在表上放置任何类型的锁。
锁和外键
Oracle数据库最大化了父键相对于依赖外键的并发控制。
锁定行为取决于外键列是否有索引。如果外键没有索引,那么子表可能会更频繁地被锁定,死锁将会发生,并发性将会降低。由于这个原因,外键几乎总是应该有索引的。唯一的例外是匹配的唯一或主键从未更新或删除。
锁和未索引的外键
数据库获得一个完整的表锁在子表没有索引存在子表的外键列,和一个会话修改父表的主键(例如,删除行或修改主键属性)或合并行父表。
当两个条件都为真时,数据库在子表上获得一个全表锁:
•子表的外键列上不存在索引。
•会话修改父表中的主键(例如,删除行或修改主键属性)或将行合并到父表中。
注意:
插入到父表中不会获得阻止子表上的DML的阻塞表锁。在插入的情况下,数据库获得子表上的一个锁,该锁可以防止结构更改,但不能修改现有或新添加的行。
假设hr.departments表是hr的父表。包含未索引的外键employee .department_id。下图显示了修改部门表中department 60的主键属性的会话。

在图9-3中,在department 60的主键修改期间,数据库获得了employees 的一个全表锁。此锁允许其他会话查询,但不更新employees表。例如,会话不能更新员工的电话号码。在对department部门表的主键修改完成后,立即释放employee上的表锁。如果部门中的多行进行了主键修改,那么对于在部门中修改的每一行,都将获得并释放一个employee表锁。
锁和索引外键
当对子表中的外键列进行索引,并且会话修改父表中的主键(例如,删除行或修改主键属性)或将行合并到父表中时,数据库不会获取子表上的完全表锁。
父表上的锁可防止事务获取独占表锁,但不会在主键修改期间阻止父表或子表上的DML。如果主键修改发生在父表上,而更新发生在子表上,则此情况更可取。
图9-4显示了带有索引department_id列的子表employees。事务将部门280从部门中删除。这种删除不会导致数据库获得“Locks和Unindexed Foreign Keys”中描述的employee表上的全表锁。
Figure 9-4 Locking Mechanisms with Indexed Foreign Key

如果子表指定了DELETE CASCADE,那么父表的删除可能导致子表的删除。例如,删除部门280会导致被删除部门的员工的记录被删除。在这种情况下,等待和锁定规则与从父表中删除行后从子表中删除行相同。
DDL锁
当正在进行的DDL操作作用于或引用对象时,数据字典(DDL)锁保护模式对象的定义。
在DDL操作期间,只有修改或引用的单个架构对象才会被锁定。数据库从不锁定整个数据字典。
Oracle数据库代表任何需要DDL锁的DDL事务自动获取DDL锁。用户不能显式请求DDL锁。例如,如果用户创建了一个存储过程,那么Oracle数据库会自动为过程定义中引用的所有模式对象获取DDL锁。DDL锁防止在过程编译完成之前更改或删除这些对象。
独占DDL锁
独占DDL锁阻止其他会话获得DDL或DML锁。
大多数DDL操作需要对一个资源使用独占DDL锁,以防止对其他DDL操作的破坏干扰,这些操作可能修改或引用相同的模式对象。例如,当ALTER TABLE添加一个列时,DROP TABLE不允许删除一个表,反之亦然。
互斥DDL锁在DDL语句执行和自动提交期间有效。在获取独占DDL锁期间,如果另一个操作在模式对象上持有另一个DDL锁,则该获取将一直等待,直到释放旧的DDL锁,然后继续。
共享DDL锁
资源的共享DDL锁可以防止对冲突的DDL操作的破坏性干扰,但允许对类似的DDL操作进行数据并发。
例如,在运行CREATE PROCEDURE语句时,包含的事务为所有引用的表获取共享DDL锁。其他事务可以并发地创建引用相同表的过程,并获取相同表上的并发共享DDL锁,但是任何事务都不能获得任何引用表上的独占DDL锁。
共享DDL锁在DDL语句执行和自动提交期间有效。因此,持有共享DDL锁的事务可以保证引用的模式对象的定义在事务期间保持不变。
可分解的解析锁
解析锁由SQL语句或PL/SQL程序单元持有,用于它引用的每个模式对象。
获取解析锁,以便在修改或删除引用的对象时,关联的共享SQL区域可以失效。一个解析锁被称为一个可分解的解析锁,因为它不禁止任何DDL操作,并且可以被分解为允许冲突的DDL操作。
在SQL语句执行的解析阶段,在共享池中获取一个解析锁。只要该语句的共享SQL区域保留在共享池中,就会持有该锁。
系统锁
Oracle数据库使用各种类型的系统锁来保护内部数据库和内存结构。用户无法访问这些机制,因为用户无法控制它们的出现或持续时间。
闩锁 Latches
闩锁是一种简单的低级序列化机制,用于协调对共享数据结构、对象和文件的多用户访问。
闩锁保护共享内存资源在被多个进程访问时不受损坏。具体来说,锁存保护数据结构不受以下情况的影响:
•多个会话并发修改
•被一个会话读取,而被另一个会话修改
•访问时释放(老化)内存
通常,一个锁存器保护SGA中的多个对象。例如,DBW和LGWR等后台进程从共享池中分配内存以创建数据结构。为了分配这个内存,这些进程使用一个共享池锁存器来序列化访问,以防止两个进程试图同时检查或修改共享池。分配内存后,其他进程可能需要访问共享池区域,如库缓存,这是解析所必需的。在这种情况下,进程只锁定库缓存,而不是整个共享池。
与队列锁(如行锁)不同,锁不允许会话排队。当闩锁可用时,请求闩锁的第一个会话获得对它的独占访问。当一个进程在一个循环中重复请求一个锁存器时,锁存器旋转的现象就会发生,而当一个进程在更新锁存器请求之前释放CPU时,锁存器睡眠就会发生。
通常,Oracle进程在操作或查看数据结构时获取锁存的时间非常短。例如,在处理单个雇员的工资更新时,数据库可以获取和释放数千个锁存。锁存的实现依赖于操作系统,特别是在进程是否等待锁存以及等待锁存的时间方面。
锁存的增加意味着并发性的减少。例如,过多的硬解析操作会为库缓存闩锁创建争用。V$LATCH视图包含每个闩锁的详细闩锁使用统计信息,包括请求和等待每个闩锁的次数。
互斥锁 Mutexes
互斥对象(互斥对象)是一种低级的机制,它可以防止内存中的对象在被并发进程访问时老化或损坏。互斥锁类似于锁存器,但锁存器通常保护一组对象,而互斥锁保护单个对象。
互斥锁有几个好处:
•互斥锁可以减少争用的可能性。
•因为一个锁存器保护多个对象,所以当进程试图同时访问这些对象时,它可能成为瓶颈。通过序列化对单个对象而不是组的访问,互斥锁提高了可用性。
•互斥锁消耗的内存比锁存器少。
•在共享模式下,互斥锁允许多个会话并发引用
内部锁 Internal Locks
内部锁是比锁存器和互斥锁更高级、更复杂的机制,可用于各种目的。
数据库使用以下类型的内部锁:
•字典缓存锁
这些锁的持续时间非常短,当条目被修改或使用时,这些锁被保存在字典缓存中的条目上。它们保证被解析的语句不会看到不一致的对象定义。字典缓存锁可以是共享的,也可以是独占的。共享锁在解析完成时释放,而独占锁在DDL操作完成时释放。
•文件和日志管理锁
这些锁保护各种文件。例如,内部锁保护控制文件,因此一次只有一个进程可以修改它。另一个锁协调在线重做日志文件的使用和归档。数据文件被锁定,以确保多个实例以共享模式挂载数据库,或一个实例以独占模式挂载数据库。因为文件锁和日志锁指示文件的状态,所以这些锁必须保持很长时间。
•表空间和撤销段锁
这些锁保护表空间和撤销段。例如,访问数据库的所有实例必须就表空间是联机还是脱机达成一致。
撤消段被锁定,因此只有一个数据库实例可以写入一个段。
5 手动数据锁概述
您可以手动覆盖Oracle数据库默认锁定机制。
Oracle数据库自动执行锁定,以确保数据并发性、数据完整性和语句级读取一致性。然而,在以下情况下重写默认锁是有用的:
•应用程序需要事务级的读一致性或可重复读。
在这种情况下,查询必须在事务持续期间生成一致的数据,而不能反映其他事务的更改。通过使用显式锁定、只读事务、可序列化事务或覆盖默认锁定,可以实现事务级别的读一致性。
•应用程序要求事务对资源具有独占访问权,这样事务就不必等待其他事务完成。
您可以在会话或事务级别覆盖Oracle数据库自动锁定。在会话级别,会话可以使用ALTER session语句设置所需的事务隔离级别。在事务级别,包含以下SQL语句的事务覆盖Oracle数据库默认锁定:
•SET TRANSACTION ISOLATION LEVEL语句
•LOCK TABLE语句(用于锁定表或与视图一起使用时锁定基表)
•SELECT ... FOR UPDATE语句
上述语句获取的锁在事务结束后释放,或者回滚到保存点释放它们。
如果在任何级别上都覆盖了Oracle数据库默认锁定,那么数据库管理员或应用程序开发人员应该确保覆盖的锁定过程正确操作。锁定过程必须满足以下标准:数据完整性得到保证,数据并发性可接受,死锁不可能发生或得到了适当的处理。
6 用户定义锁的概述
使用Oracle数据库锁管理服务,您可以为特定的应用程序定义自己的锁。
例如,您可以创建一个锁来序列化对文件系统上的消息日志的访问。由于保留的用户锁与Oracle数据库锁相同,所以它具有包括死锁检测在内的所有Oracle数据库锁功能。用户锁与Oracle数据库锁从不冲突,因为它们是用前缀UL标识的。
可以通过DBMS_LOCK包中的过程获得Oracle数据库锁管理服务。可以在PL/SQL块中包含以下语句:
•请求一个特定类型的锁
•在相同或不同实例中的另一个过程中,为锁赋予一个可识别的唯一名称
•更改锁类型
•释放锁





