事务介绍
事务是包含一个或多个 SQL 语句的逻辑的、原子的工作单元。
事务将 SQL语句分组,以便它们可以一起被提交,即将其应用到数据库,或者一起被回滚,即将其从数据库中撤消。Oracle 数据库将为每个事务分配一个称为事务 ID 的唯一标识符。
所有 Oracle 事务都符合称为 ACID 属性的数据库事务的基本属性。ACID 是以下的缩写:
- 原子性
事务中的所有任务,要么全部执行,要么都不执行。不存在部分完成的事务。例如,如果事务启动并欲更新 100 行,但在系统在完成 20行更新后出现故障,则数据库会回滚这 20 行更新。 - 一致性
事务会将数据库从一个一致状态变为另一个一致状态。例如,在从一个储蓄账户借记、并从一个支票账户贷记的银行事务中,故障一定不能导致数据库仅仅贷记一个账户,这会导致数据不一致。 - 隔离性
一个事务必须在被提交之后,其它事务才能看见其效果。例如,正在更新 hr.employees 表的一个用户,不会看到由另一个用户在 employees 表上未提交的更改。因此,对这些用户来说,这些事务好像是串行执行的。 - 持久性
已提交的事务所做的更改是永久性的。事务完成后,数据库通过其恢复机制,确保在事务中所做的更改不会丢失。
使用事务是数据库管理系统有别于文件系统的最重要方式之一。
示例事务:帐户借贷
为了说明事务的概念,考虑一个银行数据库。
当一个客户将钱从储蓄账户转移到支票帐户时,事务必须由三个单独的操作组成:
- 减少储蓄账户余额
- 增加支票账户余额
- 在事务日志中记录这笔交易
Oracle 数据库必须考虑两种情况。如果所有三个 SQL 语句都对该账号维持了正确的余额,则事务的效果可以应用到数据库中。但是,如果某个问题,如资金不足、帐号无效、或硬件故障,阻止了事务中某些语句的完成,则数据库必须回滚整个事务,以便所有帐户的余额是正确的。
下图说明了银行事务。第一条语句从储蓄帐户 3209 中减去 500 美元。第二条语句向支票帐户 3208 增加 500 美元。第三条语句在日志表中插入这笔转账记录。最后一个语句提交事务。
)
事务的结构
数据库事务由一个或多个语句组成。
具体而言,事务包含下列之一:
- 一个或多个数据操作语言 (DML) 语句一起构成的、对数据库的一个原子更改
- 一个数据定义语言 (DDL) 语句
事务有一个开始点和结束点。
事务的开始
事务开始于所遇到的第一个可执行 SQL 语句。
可执行的 SQL 语句是能产生数据库实例调用的 SQL 语句,包括 DML 和 DDL 语句,及 SET TRANSACTION 语句。
当事务开始时,Oracle 数据库将为事务分配一个可用的撤销数据段,以记录新事务的撤消条目。数据库在第一个 DML 语句过程中,首先会分配撤销段和事务表槽,然后分配事务 ID。事务 ID 对于事务来说是唯一的,并由撤销段号、槽号、和序列号来表示。
下面的示例执行一个 UPDATE 语句以开始一个事务,并从 V$TRANSACTION 查询有关事务的详细信息:
SQL> UPDATE hr.employees SET salary=salary;
107 rows updated.
SQL> SELECT XID AS "txn id", XIDUSN AS "undo seg", XIDSLOT AS "slot",
2 XIDSQN AS "seq", STATUS AS "txn status"
3 FROM V$TRANSACTION;
txn id undo seg slot seq txn status
---------------- ---------- ---------- ---------- ----------------
0600060037000000 6 6 55 ACTIVE
事务的结束
事务可以在不同的情况下结束。
事务在出现以下操作时结束:
- 用户在没有 SAVEPOINT 子句的语句中发出 COMMIT 或 ROLLBACK。
提交意味着用户会显式或隐式地请求事务中所做的更改被持久化。只有在提交事务之后,事务所做的更改才是永久性的,并对其他用户可见。 - 用户运行一个 DDL 命令,如 CREATE、DROP、RENAME、或 ALTER 等。
数据库在每个 DDL 语句之前和之后发出一个隐式的 COMMIT 语句。如果当前事务中包含 DML 语句,则 Oracle 数据库首先提交该事务,然后将该 DDL 语句作为一个新的单语句事务来运行并提交。 - 用户从大多数 Oracle 数据库实用程序和工具正常退出,导致当前事务被隐式提交。当用户断开连接时的提交行为依赖于应用程序,并且是可配置的。
- 客户端进程异常终止,导致数据库使用存储在事务表和撤销段中的元数据来隐式回滚其事务。
一个事务结束后, 下一个可执行的 SQL 语句将自动启动后续事务。下面的示例执行一个更新,来启动一个事务,并以一个 ROLLBACK 语句来结束事务,然后又执行一个更新启动一个新事务 (请注意,事务 Id 变了):
SQL> UPDATE hr.employees SET salary=salary;
107 rows updated.
SQL> SELECT XID, STATUS FROM V$TRANSACTION;
XID STATUS
---------------- ----------------
0800090033000000 ACTIVE
SQL> ROLLBACK;
Rollback complete.
SQL> SELECT XID FROM V$TRANSACTION;
no rows selected
SQL> UPDATE hr.employees SET last_name=last_name;
107 rows updated.
SQL> SELECT XID, STATUS FROM V$TRANSACTION;
XID STATUS
---------------- ----------------
0900050033000000 ACTIVE
语句级原子性
Oracle 数据库支持语句级原子性,这意味着 SQL 语句是一个原子工作单元,要么完全成功,要么完全失败。
成功的语句不同于已提交事务。如果一个 SQL 语句能被数据库正确分析,并作为一个原子单位来运行且未产生错误,且所有行都被正确更改,则这个 SQL 语句是成功执行的。
如果 SQL 语句在执行过程中导致错误,则它是不成功的,该语句中的所有影响都将被回滚。此操作是一个语句级回滚。此操作具有以下特征:
- 未成功的 SQL 语句只会导致它本身执行的工作丢失。
未成功的语句不会导致丢失当前事务中该语句之前的任何工作。例如,若“Sample Transaction: Account Debit and Credit”中第二个 UPDATE 语句的执行导致错误并被回滚,但由第一个 UPDATE 语句执行的工作是不会被回滚的。第一个 UPDATE 语句可以由用户显式提交或回滚。 - 回滚的效果是,好像该语句从来未运行过。
原子语句的副作用被视为原子语句的一部分,例如,由语句执行所引发的触发器调用。作为原子语句的一部分所产生的所有工作,要么都成功,要么都失败。
由错误导致语句级回滚的一个示例,是尝试插入重复的主键。死锁中的某个为争用相同数据的 SQL 语句,也会导致语句级回滚。然而,在解析中发现的错误,比如语法错误,实际上尚未运行,因此不会导致语句级回滚。
系统更改号(SCNs)
系统更改号 (SCN) 是一个由 Oracle 数据库使用的逻辑、内部的时间戳。
SCN 按数据库中发生的事件排序,以满足在事务 ACID 属性的需要。Oracle 数据库使用 SCN 来标记某个位置,在其之前的所有更改都被认为已写到磁盘上,以避免在恢复过程中应用不必要的重做。数据库还使用 SCN 来标记一个其数据不存在重做信息的点,以便恢复过程可以在该点停止。
SCN 是按单调递增的顺序发生的。Oracle 数据库可以像使用时钟一样使用 SCN,因为一个 SCN 观察值指示一个逻辑时间点,而其重复观察值相比之前会相同或更大。若一个事件的 SCN 比另一个事件的 SCN 低,则它在数据库中发生在一个更早的时间。几个事件可以共享相同的 SCN,这意味着他们在数据库中同时发生。
每个事务都有一个 SCN。例如,如果事务更新了一行,则数据库记录此更新发生时的 SCN。此事务中其他修改具有相同的 SCN。当事务提交时,数据库将为此提交记录一个 SCN。
Oracle 数据库在系统全局区域(SGA)中递增 SCN。事务中修改数据时,数据库会将一个新的 SCN 写入到分配给事务的撤销数据段。然后日志写入器进程立即将事务的提交记录写入在线重做日志。提交记录具有事务的唯一 SCN。Oracle 数据库还使用 SCN 作为其实例恢复和介质恢复机制的一部分。
事务控制概述
事务控制即管理 DML 语句所做的更改,和将 DML 语句分组为事务。
一般情况下,应用程序设计人员都关注事务控制,以便工作能按逻辑单元来完成,且数据能保持一致。
事务控制涉及使用下面的语句,如“Transaction Control Statements”中所述:
- COMMIT 语句结束当前事务,并使在事务中执行的所有更改都具有持久性。提交还会清除在事务中的所有保存点,并释放事务锁。
- ROLLBACK 语句将取消当前事务中所做的工作;它导致所有自上次提交或回滚以来的数据更改被丢弃。ROLLBACK TO SAVEPOINT 语句将撤消自上次保存点以来所做的更改,但不会结束整个事务。
- SAVEPOINT 语句标识在事务中您可以稍后回滚到的点。
| T | Session | 解释 |
|---|---|---|
| t0 | |
此语句结束会话中的任何现有事务。 |
| t1 | |
此语句开始一个事务,并命名为 sal_update。 |
| t2 | |
此语句将 Banda 的薪金更新为 7000。 |
| t3 | |
此语句创建一个名为 after_banda_sal 的保存点,使该事务中的更改可以回滚到该点。 |
| t4 | |
此语句将 Greene 的薪金更新为 12000。 |
| t5 | |
此语句创建一个名为 after_greene_sal 的保存点,使该事务中的更改可以回滚到该点。 |
| t6 | |
此语句将事务回滚至 t3,,撤消在 t4 时对 Greene 薪金的更新。sal_update 事务并未结束。 |
| t7 | |
此语句在事务 sal_update 中将 Greene 的薪金更新为 11000。 |
| t8 | |
此语句回滚事务 sal_update 中的所有更改,以结束事务。 |
| t9 | |
此语句开始新的会话中的事务,并命名为 sal_update2。 |
| t10 | |
此语句将 Banda 的薪金更新为 7050。 |
| t11 | |
此语句将 Greene 的薪金更新为 10950。 |
| t12 | |
此语句提交事务 sal_update2 中的所有更改,以结束事务。提交保证所做的更改被保存在联机重做日志文件中。 |
事务名称
事务名称是一个由用户指定的可选标记,作为工作中正在执行的事务中的一个提示。你使用 SET TRANSACTION … NAME 语句来为事务命名,如果要用它,则它必须是事务的第一个语句。
在表 10-1 中的第一个事务被命名为 sal_update,而第二个被命名为 sal_update2。
事务名称提供了以下优点:
- 有助于监测长时间运行的事务,并解决可疑的分布式事务。
- 您可以在应用程序中查看事务名称及其相应事务 ID。例如,数据库管理员可以在监视系统活动时,在 Oracle 企业管理器 (企业管理器)中查看事务名称。
- 数据库将事务名称写入事务的审计重做记录,因此您可以使用 LogMiner 来搜索在重做日志中的特定事务。
- 可以使用事务名称在 V$TRANSACTION 之类的数据字典视图中查找特定的事务。
活动事务
活动事务即是已开始但尚未提交或回滚的事务。
在表 10-1 中,sal_update 事务中第一个修改数据的语句是更新 Banda 的薪金。从该更新成功执行、到 ROLLBACK 语句结束该事务,sal_update 事务处于活动状态。
在事务提交或回滚之前,事务对数据所做的更改是暂时的。在事务结束之前,数据的状态如下表所示:
| 状态 | 描述 | 参阅 |
|---|---|---|
| Oracle 数据库已在SGA中生成撤消数据信息。 | 消数据包含由事务中的 SQL 语句所更改的数据旧值。 | “Read Consistency in the Read Committed Isolation Level” |
| Oracle 数据库已在 SGA 的联机重做日志缓冲区中生成了重做信息。 | 重做日志记录包含数据块和撤消块的更改。 | “Redo Log Buffer” |
| 已经对 SGA 中的数据库缓冲区进行了更改。 | 已提交事务所做的数据更改,存储在 SGA 的数据库缓冲区中,但不一定会立即由数据库写入器写入数据文件。磁盘写入可能会在提交之前或之后发生。 | “Database Buffer Cache” |
| 数据更改所影响的行已被锁定。 | 其他用户不能更改受影响的行中的数据,也不能查看未提交的更改。 | “Summary of Locking Behavior” |
保存点
保存点是事务上下文中的一个由用户声明的中间标记。
在内部,此标记将被解析为一个 SCN。保存点将长事务划分为多个更小的部分。
如果你在一个长事务中使用保存点,则您就可以在之后将事务中执行的工作回滚到当前时间之前,却又在事务中声明的某个保存点之后。因此,如果您出了错,您不需要重新提交所有每个语句。表 10-1 创建保存点 after_banda_sal,以便对 Greene 薪金的更新可以回滚到此保存点。
回滚到保存点
在未提交事务中回滚到某个保存点,意味着该指定的保存点之后所做的任何更改都将被撤消,但它并不意味着事务本身的回滚。
当一个事务回滚到一个保存点时,比如像表 10-1 中运行的 ROLLBACK TO SAVEPOINT after_banda_sal,将执行以下操作:
- Oracle 数据库将只回滚在该保存点之后运行的语句。
在表 10-1 中,ROLLBACK TO SAVEPOINT 将导致对 Greene 的更新被回滚,但对 Banda 的更新不会被回滚。 - Oracle 数据库保留在 ROLLBACK TO SAVEPOINT 语句中指定的保存点,但随后的所有保存点都将丢失。
在表 10-1 中,ROLLBACK TO SAVEPOINT 子句将导致 after_greene_sal 保存点丢失。 - Oracle 数据库释放在指定保存点之后获取的所有表锁和行锁,但保留在该保存点之前获取的所有数据锁。
事务仍处于活动状态,并可以继续。
排队事务
取决于当时的场景,之前在等待被锁定资源的事务,在回滚到保存点后,可能仍处于阻塞状态。
一个事务被另一个事务阻塞时,它会在阻塞事务后面排队,因此整个阻塞事务必须提交或回滚,被阻塞的事务才能得以继续。
在下表所示的场景中,会话 1 回滚到在它执行一个 DML 语句之前所创建的保存点。然而会话 2 仍被阻塞,因为它正在等待事务 1 完全完成。
| T | Session 1 | Session 2 | Session 3 | Explanation |
|---|---|---|---|---|
| t0 | |
会话 1 开始一个事务。会话将在 Banda 行 (TX) 上置一个独占锁,而在表上置一个子独占表锁(SX)。 | ||
| t1 | |
会话 1 创建一个名为 after_banda_sal 的保存点。 | ||
| t2 | |
会话 1 锁定 Greene 行。 | ||
| t3 | |
会话 2 尝试更新 Greene 行,但未能锁定该行,因为会话 1 已在该行上具有一个锁。会话 2 中还没有开始任何事务。 | ||
| t4 | |
会话 1 回滚对 Greene 薪金的更新,并释放了对 Greene 行的锁定。在 t0 时刻获取的表锁仍未释放。 此时会话 2 仍被会话 1 阻塞,因为会话 2 排在会话 1 中的事务之后,而该事务尚未完成。 |
||
| t5 | |
Greene 行当前并未锁定,因此会话 3 为更新 Greene 行获取了一个该行上的锁。此语句在会话 3 中启动一个事务。 | ||
| t6 | |
会话 1 提交,结束其事务。在 Greene 行的更新队列中,会话 2 现在排在会话 3 中的事务之后。 |
事务回滚
未提交事务的回滚,将撤消已由事务中的 SQL 语句执行过的任何数据更改。
事务回滚后,事务中所做工作的影响就不再存在。对未引用任何保存点的整个事务的回滚,Oracle 数据库执行下列操作:
- 通过使用相应的撤销段,撤消事务中所有 SQL 语句所做的所有更改
每个活动事务的事务表条目包含一个指向该事务的所有撤消数据(与应用顺序相反)的指针。数据库从撤销段中读取数据,反转其操作,然后将撤消条目标记为已应用。因此,如果一个事务插入行,则其回滚删除行。如果一个事务更新行,则其回滚反转这个更新。如果一个事务删除一个行,则其回滚重新插入该行。在表 10-1 中,ROLLBACK 反转对 Greene 和 Banda 的薪金更新。 - 释放由事务持有的所有数据锁
- 清除在事务中的所有保存点
在表 10-1 中,ROLLBACK 删除保存点 after_banda_sal。after_greene_sal 保存点被 ROLLBACK TO SAVEPOINT 语句删除。 - 结束事务
在表 10-1 中,ROLLBACK 使数据库处于与刚刚执行了 COMMIT 后相同的状态。
回滚的持续时间与被修改的数据量成正比。
事务提交
提交结束当前事务,并使事务中执行的所有更改持久化。
在表 10-1 中,第二个事务从 sal_update2 开始,并以显式 COMMIT 语句结束。在两个 UPDATE 语句中所做的更改现在被持久化了。
当一个事务提交时,将发生以下操作:
- 为提交生成 SCN。
在关联的回滚表空间的内部事务表中记录已提交的事务。分配相应的唯一事务 SCN,并将其记录在事务表中。 - 日志写入器 (LGWR)进程将重做日志缓冲区中的剩余重做日志条目写入到在线重做日志中,并将事务 SCN 写入到在线重做日志中。这个原子事件是提交事务的本质所在。
- Oracle 数据库释放在行和表上持有的锁。
还在排队等待由未提交事务持有的锁的用户,现在可以继续他们的工作了。 - Oracle 数据库删除保存点。
在表 10-1 中,在 sal_update 事务中不存在保存点,所以也没有保存点要清除。 - Oracle 数据库执行一个提交清理。
如果包含已提交的事务的被修改数据块仍处于 SGA 中,也没有其他会话正在对他们进行修改,则数据库从该块中删除与锁相关的事务信息。
理想情况下,COMMIT 会清理该数据块,以便后续的 SELECT 操作不必再执行此任务。如果某一行不存在 ITL 条目,那么它就没有被锁定。如果某个特定行的 ITL 条目存在,那么它可能被锁定,因此会话必须检查 undo 段标头,以确定是否提交了这个感兴趣的事务。如果感兴趣的事务已提交,则会话将清除该块,从而生成重做。但是,如果 COMMIT 在之前清除了 ITL,那么检查和清除就没有必要了。 - Oracle 数据库将事务标记为完成
事务提交后,用户可以查看所做的更改。
通常,提交是一个快速的操作,而与事务大小无关。提交的速度不会随着在事务中修改的数据的大小的增大而增加。提交的最长部分是由 LGWR 执行的物理磁盘 I/O。然而,LGWR 所用的时间量却减少了,因为它一直在在后台以增量方式写出重做日志缓冲区中的内容。
默认行为是,LGWR 必须将重做数据同步地写入联机重做日志,而事务必须等到被缓冲的重做数据被写到磁盘上之后,提交操作才能返回到用户。但是,为取得更低的事务提交延迟,应用程序开发人员可以指定重做被异步写入,以便事务不需要等待重做被写入磁盘,就可以立即从 COMMIT 调用返回。
事务保护概述
事务保护是应用程序可以用来提供事务幂等性的 API,事务幂等性是数据库保存有保证的提交结果的能力,该结果指示事务是否提交并完成。Oracle 数据库为 JDBC thin、OCI、OCCI 和 ODP.Net 提供 API。
可恢复错误,是指由外部系统故障引起的,与正在执行的应用程序会话逻辑无关。可恢复错误发生在前台进程、网络、节点、存储和数据库的计划和计划外停机之后。如果中断了客户机应用程序和数据库之间的连接,那么应用程序将收到一条断开连接的错误消息。连接中断时正在运行的事务称为正在运行的事务。
要决定是重演事务,还是将结果(提交或未提交)返回给客户机,应用程序必须确定正在运行的事务的结果。在 Oracle Database 12c 之前,返回给客户机的提交消息是不持久的。检查事务并不能保证它在检查后不会提交,会允许重复的事务和其他形式的逻辑破坏。例如,用户在线购买一本书时可能刷新web浏览器,并对同一本书收取两次费用。
事务保护的优点
从 Oracle Database 12c 开始,事务保护为应用程序提供了一个工具,用于确定可恢复停机后正在运行的事务的状态。
使用事务保护,应用程序可以确保事务执行不超过一次。例如,如果在线书店应用程序确定先前提交的提交失败,则应用程序可以安全地重演。
事务保护为最多一次执行提供了一个工具,以避免应用程序执行重复的提交。事务保护为每个事务提供一个已知的结果。
事务保护是 Oracle 数据库的核心功能。应用程序连续性在屏蔽终端用户中断时使用事务保护。如果没有事务保护,应用程序在出错后重试可能会导致提交重复的事务。
事务保护如何工作
本节解释丢失提交消息的问题,以及事务保护如何使用逻辑事务id来解决这个问题。
丢失提交信息
在为幂等性设计时,开发人员必须在提交commit语句之后解决通信失败的问题。提交消息不会持久保存在数据库中,因此在失败后无法检索。
下图是客户机应用程序和数据库之间交互的高级表示。

在标准提交情况下,数据库提交事务并向客户机返回一条成功消息。在图10-2中,客户机提交一条commit语句,并接收一条消息,说明通信失败。这种类型的故障可能由于多种原因发生,包括数据库实例故障或网络中断。在此场景中,客户端不知道事务的状态。
在通信失败之后,数据库可能仍然在运行提交,并且不知道客户机已断开连接。检查事务状态并不保证活动事务在检查后不会提交。如果客户端因为这个过期的信息重新发送提交,那么数据库可能会重复该事务,从而导致逻辑损坏。
逻辑事务 ID
Oracle 数据库通过使用称为逻辑事务 ID 的全局惟一标识符来解决通信故障。
此 ID 包含在会话首次连接时分配的逻辑会话号,以及在会话每次提交或回滚时更新的正在运行的提交号。从应用程序的角度来看,逻辑事务ID是在会话上提交的最后一个数据库事务失败时的惟一标识。
对于从客户端提交一个或多个事务的每个往返,数据库存储一个逻辑事务ID。对于提交数据的每个往返,这个ID可以为应用程序和数据库之间的交互提供事务幂等性。
最多一次的协议要求数据库执行以下操作,从而允许访问提交结果:
- 在同意重试的保留期间维护逻辑事务 ID
- 在提交时保存逻辑事务 ID
在事务运行时,数据库和客户机都持有逻辑事务 ID。数据库在身份验证时、从连接池中借用时以及每次往返于执行一个或多个提交操作的客户机驱动程序时,都向客户机提供一个逻辑事务 ID。
在应用程序可以确定发生可恢复错误后的最后一个事务的结果之前,应用程序使用 Java、OCI、OCCI 或 ODP.Net API 获取客户端持有的逻辑事务 ID。然后,应用程序调用 PL/SQL 过程DBMS_APP_CONT.GET_LTXID_OUTCOME,使用逻辑事务 ID 来确定最后一次提交的结果:提交(true 或 false)和用户调用完成(true 或 false)。
当使用事务保护时,当错误可恢复且会话上的最后一个事务尚未提交时,应用程序可以重演事务。当最后一个事务已提交且用户调用已完成时,应用程序可以继续。应用程序可以使用事务保护将已知的结果返回给客户机,以便客户机可以决定下一步要采取的操作。
事务保护:示例
在此场景中,由于可恢复错误,提交消息丢失。
事务保护程序使用逻辑事务 ID 来保留 COMMIT 语句的结果,确保事务有已知的结果。

在图 10-3 中,数据库通知应用程序事务是否已提交以及最后一次用户调用是否完成。然后,应用程序可以将结果返回给最终用户。这些可能性是:
- 如果事务已提交,用户调用完成,则应用程序可以将结果返回给最终用户并继续。
- 如果事务已提交但用户调用未完成,则应用程序可以向最终用户返回带有警告的结果。示例包括丢失的绑定或已处理的行数。一些应用程序依赖于额外的信息,而另一些则不依赖。
- 如果用户调用未提交,则应用程序可以将此信息返回给最终用户,或安全地重演。协议得到了保证。当提交状态返回 false 时,最后一个提交将被阻止提交。
应用程序连续性概述
应用程序连续性试图通过在非计划和计划中断后重演不完整的应用程序请求来掩盖应用程序的中断。在这种情况下,请求是来自应用程序的工作单元。
通常,请求对应于在单个数据库连接上的DML语句和单个web请求的其他数据库调用。一般而言,请求由连接池中数据库连接的签出和签入之间的调用来划分。
应用程序连续性的优点
开发人员面临的一个基本问题是如何向最终用户隐藏丢失的数据库会话。
应用程序连续性试图通过恢复数据库会话(在任何组件中断数据库和客户机之间的对话时)来解决会话丢失的问题。恢复后的数据库会话包含所有状态、游标、变量以及存在的最新事务。
应用程序连续性用例
在典型的情况下,客户机向数据库提交了一个请求,数据库已经建立了事务性和非事务性状态。
客户机上的状态保持为当前状态,可能包含输入的数据、返回的数据、缓存的数据和变量。但是,应用程序需要的操作数据库会话的状态丢失了。
如果客户端请求已启动一个或多个事务,则应用程序面临以下可能性:
- 如果已发出提交,则返回给客户机的提交消息不是持久的。客户端不知道请求是否已提交,以及在非事务性处理状态中请求到达何处。
- 如果没有发出提交,或者发出了提交但没有执行,那么将回滚正在运行的事务,并且必须使用处于正确状态的会话重演。
如果重新执行成功,则针对计划内和计划外停机的数据库用户服务不会中断。如果数据库检测到应用程序看到数据中的更改并可能对其进行操作,则拒绝重新执行。当超过允许启动重新执行的时间、应用程序使用受限调用或应用程序使用 disableReplay 方法显式禁用重新执行时,不尝试重新执行。
计划维护的应用连续性
计划中断的应用程序连续性使应用程序能够继续对数据库会话进行操作,这些会话可以可靠地用尽或迁移。
定期维护不需要中断应用程序的工作。应用程序连续性使活动工作时间从当前位置排到当前不受维护影响的新位置。在耗尽间隔结束时,会话可能仍然保留在计划维护的数据库实例上。与强制断开这些会话不同,应用程序连续性可以将这些会话故障转移到幸存的站点,并重演任何正在运行的事务。
启用了应用程序连续性后,数据库可以做以下工作:
- 在维护期间,对传入或现有工作都不报告错误
- 将活动数据库会话重定向到其他功能服务
- 根据需要,在维护期间和之后重新平衡数据库会话
使用 SRVCTL 实用程序、全局数据服务控制实用程序(GDSCTL)和 Oracle Data Guard Broker 的 drain_timeout 和 stop_option 服务属性控制计划维护期间的排放行为。DBMS_SERVICE 包提供底层基础架构。
应用连续性架构
应用程序连续性的关键组件是运行时、重连接和重演。
各阶段如下:
-
正常运行时
在这个阶段,应用程序连续性执行以下任务:
- 标识数据库请求
- 决定是否可以重演本地和数据库调用
- 构建代理对象,以便在必要时启用重演和管理队列
- 保存原始调用并验证这些调用,直到禁用数据库请求或重演结束
-
重新连接
此阶段由可恢复错误触发。应用程序连续性执行以下任务:
- 确保对数据库请求启用重演
- 管理超时
- 获取到数据库的新连接,然后验证这是一个有效的数据库目标
- 使用事务保护来确定最后提交的事务是否成功(提交的事务不重新提交)
-
重演
应用程序连续性执行以下任务:
- 重演队列中保存的调用
- 如果重演过程中出现用户可见的结果更改,则禁用重演
- 不允许提交,但允许最后一次收回(遇到错误)提交
在成功重新执行之后,请求将从失败点继续。
自治事务概述
自治事务是一个独立的事务,可以从另一个称为主事务的事务中调用。你可以挂起调用事务、在自治事务中执行 SQL 操作并提交或回滚它们,然后继续执行调用事务。
自治事务对于那些必须独立执行、而不管调用事务是否提交或回滚的操作非常有用。例如,在股票购买事务中,你想要总是提交客户数据,而无论是否股票购买最终成功通过。或者,您想要将错误消息记录到一个调试表,即使整个事务被回滚。
自治事务具有以下特征:
- 自治事务不会看见在主事务中所做的未提交更改,也不与主事务共享锁或资源。
- 自治事务中的更改在其提交后对其它事务是可见的。因此,用户可以立刻访问更新的信息,而不必等到主事务提交。
- 自治事务可以启动其他自治事务。没有关于自治事务能调用多少级别的任何限制,这只受限于可用的资源。
在 PL/SQL 中,自治事务在一个标有 AUTONOMOUS_TRANSACTION 的编译指令例程所在的自治范围内执行。在这种上下文中,例程包括顶层匿名 PL/SQL 块、PL/SQL 子程序、和触发器。一个编译指令是指示编译器执行某个编译选项的指令。AUTONOMOUS_TRANSACTION 编译指令指示数据库,当此过程执行时,作为一个独立于其父事务的新的自治事务来执行。
下图显示了控制如何从主程序 (MT)流向一个自治例程,又如何返回。主程序是 proc1 ,自治例程是 proc2。自治例程在控制返回主例程之前可以提交多个事务 (AT1 和 AT2)。

当您进入一个自治例程的可执行部分时,主例程将挂起。当您退出自治例程时,主例程就恢复了。
在图 10-4 中,proc1 内的提交将持久化自己的工作,及在其会话中执行的任何未结工作。然而,在 proc2 中的提交仅持久化在 proc2 事务中执行的工作。在事务 AT1 和 AT2 中的 COMMIT 语句对 MT 事务没有影响。
分布式事务概述
分布式事务,包括一个或多个使用称为数据库链接的模式对象的语句,更新位于分布式数据库中两个或更多不同节点上的数据。
分布式数据库是在分布式系统中,可以对应用程序而言,作为单一数据源出现的一组数据库。数据库链接描述一个数据库实例如何登录到另一个数据库实例的方式。
与本地数据库上的事务不同,分布式事务变更多个数据库上的数据。因此,分布式事务处理更复杂,因为数据库必须进行协调,以将事务中所做的更改作为一个原子单元提交或回滚。必须提交或回滚整个事务。Oracle 数据库必须通过网络协调事务控制,并保持数据的一致性,即使发生网络或系统故障。
两阶段提交
两阶段提交机制保证参与分布式事务的所有数据库,要么都提交、或者要么都回滚事务中的语句。该机制还可以保护由完整性约束、远程过程调用、和触发器执行的隐式 DML。
在涉及多个数据库的两阶段提交中,由某个数据库协调该分布式事务。发起的节点称为全局协调者。协调者询问其他数据库是否已准备好提交。如果任何数据库响应说未准备好,则整个事务回滚。如果所有数据库都说已准备好,则协调者将广播一个消息,使该提交在每个数据库上持久化。
两阶段提交机制对发出分布式事务的用户是透明的。事实上,用户甚至不需要知道事务是分布式的。表示事务结束的 COMMIT 语句自动触发两阶段提交机制。在数据库应用程序正文中不需要额外编码或复杂的语句语法来包括分布式事务。
可疑事务
可疑分布式事务,发生在两阶段提交被任何类型的系统或网络故障中断时。
例如,两个数据库向协调数据库报告他们已准备提交,但协调数据库实例收到消息后却立即失效了。这两个准备提交的数据库现在被挂起,而他们还在等待通知结果。
恢复器 (RECO) 后台进程会自动解决可疑分布式事务的残局。该故障修复和通信重新建立后,在所有涉及到的节点上,每个本地的 Oracle 数据库的 RECO 进程一致地自动提交或回退任何可疑的分布式事务。
对于长时间失败的情况,Oracle 数据库使每个本地管理员手动提交或回滚任何由于失败引起的可疑分布式事务。此选项使本地数据库管理员能够释放任何由于长期的失败所引起的无限期持有资源锁定。
如果数据库必须恢复到过去的某个时间,则数据库恢复工具使得在其它站点的数据库管理员能将其数据库返回到某个过去的时间点。此操作可确保全局数据库保持一致。




