oracle 专家高级编程 中文第八章.pdf
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《oracle 专家高级编程 中文第八章.pdf》由会员分享,可在线阅读,更多相关《oracle 专家高级编程 中文第八章.pdf(19页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 1第 8 章 事务 事务(Transaction)是数据库区别于文件系统的特性之一。在文件系统中,如果你正把文件写到一半,操作系统突然崩溃了,这个文件就很可能会被破坏。不错,确实还有一些“日报式”(journaled)之类的文件系统,它们能把文件恢复到某个时间点。不过,如果需要保证两个文件同步,这些文件系统就无能为力了。倘若你更新了一个文件,在更新完第二个文件之前,系统突然失败了,你就会有两个不同步的文件。这是数据库中引入事务的主要目的:事务会把数据库从一种一致状态转变为另一种一致状态。这就是事务的任务。在数据库中提交工作时,可以确保要么所有修改都已经保存,要么所有修改都不保存。另外,还能保
2、证实现了保护数据完整性的各种规则和检查。在上一章中,我们从并发控制角度讨论了事务,并说明了在高度并发的数据访问条件下,根据 Oracle 的多版本读一致模型,Oracle事务每次如何提供一致的数据。Oracle 中的事务体现了所有必要的 ACID 特征。ACID 是以下 4 个词的缩写:q 原子性(atomicity):事务中的所有动作要么都发生,要么都不发生。q 一致性(consistency):事务将数据库从一种一致状态转变为下一种一致状态。q 隔离性(isolation):一个事务的影响在该事务提交前对其他事务都不可见。q 持久性(durability):事务一旦提交,其结果就是永久性的
3、。上一章讨论过 Oracle 如何得到一致性和隔离性。这里我们主要关注原子性的概念,并说明 Oracle 中是如何应用这个概念的。这一章我们将讨论原子性的含义,以及 Oracle 中原子性对语句有什么影响。首先会介绍 COMMIT、SAVEPOINT 和 ROLLBACK 等事务控制语句,并讨论事务中如何保证完整性约束。我们会读到,如果你原来一直在其他数据库中开发,可能在事务方面养成一些坏习惯(即后面所说的“不好的事务习惯”)。这里还将介绍分布式事务和两段提交(two-phase commit,2PC)。最后会分析自治事务,指出什么是自治事务以及自治事务所扮演的角色。8.1 事务控制语句 Or
4、acle 中不需要专门的语句来“开始事务”。隐含地,事务会在修改数据的第一条语句处开始(也就是得到 TX 锁的第一条语句)。也可以使用 SET TRANSACTION 或 DBMS_TRANSACTION 包来显示地开始一个事务,但是这一步并不是必要的,这与其他的许多数据库不同,因为那些数据库中都必须显式地开始事务。如果发出 COMMIT 或 ROLLBACK 语句,就会显式地结束一个事务。注意注意 ROLLBACK TO SAVEPOINT 命令不会结束事务!正确地写为 ROLLBACK(只有这一个词)才能结束事务。一定要显式地使用 COMMIT 或 ROLLBACK 来终止你的事务;否则,
5、你使用的工具/环境就会从中挑一个来结束事务。如果正常地退出 SQL*Plus 会话,而没有提交或回滚事务,SQL*Plus 就会认为你希望提交前面做的工作,并为你完成提交。另一方面,如果你只是退出一个 Pro*C 程序,就会发生一个隐式的回滚。不要过分依赖这些隐式行为,因为将来这些行为可能会有改变。一定要显式地 COMMIT或 ROLLBACK 你的事务。Oracle 中的事务是原子性的。这说明无非两种情况:构成事务的每条语句都会提交(成为永久),或者所有语句都回滚。这种保护还延伸到单个的语句。一条语句要么完全成功,要么这条语句完全回滚。注意,我说的是“语句”回滚。如果一条语句失败,并不会导致
6、先前已经执行的语句自动回滚。它们的工作会保留,必须由你来提交或回滚。这里谈到了语句和事务是原子性的,在具体介绍其含义之前,先来看看我们可以使用哪些事务控制语句:q COMMIT:要想使用这个语句的最简形式,只需发出 COMMIT。也可以更详细一些,写为 COMMIT WORK,不过这二者是等价的。COMMIT 会结束你的事务,并使得已做的所有修改成为永久性的(持久保存)。COMMIT 语句还有一些扩展用于分布式事务中。利用这些扩展,允许增加一些有意义的注释为 COMMIT 加标签(对事务加标签),以及强调提交一个可疑的分布式事务。q ROLLBACK:要想使用这个语句的最简形式,只需发出 RO
7、LLBACK。同样地,你也可以罗嗦一些,写为 ROLLBACK WORK,但是二者是等价的。回滚会结束你的事务,并撤销正在进行的所有未提交的修改。为此要读取存储在回滚段/undo 段中的信息,并把数据库块恢复到事务开始之前的状态(后面我将把回滚段/undo 段统称为 undo 段,Oracle 10g 中都喜欢用这个词)。q SAVEPOINT:SAVEPOINT 允许你在事务中创建一个“标记点”(marked point),一个事务中可以有多个 SAVEPOINT。2q ROLLBACK TO:这个语句与 SAVEPOINT 命令一起使用。可以把事务回滚到标记点,而不回滚在此标记点之前的任何
8、工作。所以,可以发出两条 UPDATE 语句,后面跟一个 SAVEPOINT,然后又是两条 DELETE 语句。如果执行 DELETE语句期间出现了某种异常情况,而且你捕获到这个异常,并发出 ROLLBACK TO SAVEPOINT 命令,事务就会回滚到指定的SAVEPOINT,撤销 DELETE 完成的所有工作,而 UPDATE 语句完成的工作不受影响。q SET TRANSACTION:这条语句允许你设置不同的事务属性,如事务的隔离级别以及事务是只读的还是可读写的。使用手动 undo管理时,还可以使用这个 来指示事务使用某个特定的 undo 段,不过不推荐这种做法。我们将在第 9 章更详
9、细地讨论手动和自动 undo 管理。就这么多,没有别的事务控制语句了。最常用的控制语句就是 COMMIT 和 ROLLBACK。SAVEPOINT 语句的用途有点特殊。Oracle在内部频繁地使用了这个语句,你会发现这语句在你的应用中可能也有用。8.2 原子性 前面对事务控制语句做了一个简要的概述后,下面可以看看语句原子性、过程原子性和事务原子性到底有什么含义。8.2.1 语句级原子性 考虑以下语句:Insert into t values(1);看上去很明显,如果它由于一个约束冲突而失败,这一行就不会插入。不过,再考虑下面的例子,这里表T上的一个INSERT或DELETE会触发一个触发器,它
10、将适当地调整表 T2 中的 CNT 列:ops$tkyteORA10G create table t2(cnt int);Table created.ops$tkyteORA10G insert into t2 values(0);1 row created.ops$tkyteORA10G commit;Commit complete.ops$tkyteORA10G create table t(x int check(x0);Table created.ops$tkyteORA10G create trigger t_trigger 2 before insert or delete on
11、t for each row 3 begin 4 if(inserting)then 5 update t2 set cnt=cnt+1;6 else 7 update t2 set cnt=cnt-1;8 end if;9 dbms_output.put_line(I fired and updated|10 sql%rowcount|rows);11 end;12/Trigger created.在这种情况下,会发生什么就不那么显而易见了。如果触发器触发之后出现了错误,触发器的影响是否还存在?也就是说,如果触发器被触发,并且更新了 T2,但是这一行没有插入到 T 中,结果会是什么?显然答案
12、应该是,如果并没有真正在 T 中插入一行,我们就 3希望 T2 中的 CNT 列递增。幸运的是,在 Oracle 中,客户最初发出的语句(在这里就是 INSERT INTO T)会完全成功或完全失败。这个语句是原子性的。以下可以验证这一点:ops$tkyteORA10G set serveroutput on ops$tkyteORA10G insert into t values(1);I fired and updated 1 rows 1 row created.ops$tkyteORA10G insert into t values(-1);I fired and updated 1
13、rows insert into t values(-1)*ERROR at line 1:ORA-02290:check constraint(OPS$TKYTE.SYS_C009597)violated ops$tkyteORA10G select*from t2;CNT-1 注意注意 使用 Oracle9i Release 2 及以前版本的 SQL*Plus 时,要想看到触发器被触发,需要在第二个插入后面增加一行代码:exec null。这是因为,在这些版本中,SQL*Plus 不会在失败的 DML 语句之后获取和显示 DBMS_OUTPUT 信息。Oracle 10g 版本则不然,这个
14、版本的 SQL*Plus 确实会显示 DBMS_OUTPUT 信息。这样一来,T 中成功地插入一行,而且我们也适当地接收到信息:I fired and updated 1 row。下一个 INSERT 语句违反了 T 上的完整性约束。此时出现了 DBMS_OUTPUT 信息T 上的触发器确实触发了,这个 DBMS_OUTPUT 信息就是证据。触发器成功地完成了 T2的更新。我们可能认为现在 T2 中 CNT 的值是 2,但是可以看到它的值实际上为 1。Oracle 保证最初的 INSET(即导致触发器触发的插入语句)是原子性的,这个 INSERT INTO T 是语句,所以 INSERT IN
15、TO T 的任何副作用都被认为是语句的一部分。为了得到这种语句级原子性,Oracle 悄悄地在每个数据库调用外面包了一个 SAVEPOINT。前面的两个 INSERT 实际上处理如下:Savepoint statement1;Insert into t values(1);If error then rollback to statement1;Savepoint statement2;Insert into t values(-1);If error then rollback to statement2;对于习惯于使用 Sybase 或 SQL Server 的程序员来说,刚开始可能会有点
16、摸不着头脑。在这些数据库中,情况恰恰相反。这些系统中的触发器会独立于触发语句执行。如果触发器遇到一个错误,它必须显式地回滚自己的工作,然后产生另外一个错误来回滚触发语句。否则,即使触发语句(或该语句的另外某个部分)最终会失败,触发器完成的工作也会持久保留。在 Oracle 中,这种语句级原子性可以根据需要延伸。在前面的例子中,如果 INSERT INTO T 触发了一个触发器,这个触发器会更新另一个表,而那个表也有一个触发器,它会删除第三个表(以此类推),那么要么所有工作都成功,要么无一成功。为保证这一点,无需你编写任何特殊的代码,Oracle 本来就会这么做。8.2.2 过程级原子性 有意思
17、的是,Oracle 把 PL/SQL 匿名块也当作是语句。请考虑以下存储过程:ops$tkyteORA10G create or replace procedure p 2 as 3 begin 4 insert into t values(1);5 insert into t values(-1);6 end;47/Procedure created.ops$tkyteORA10G select*from t;no rows selected ops$tkyteORA10G select*from t2;CNT-0 以上创建了一个过程,而且我们知道这个过程不会成功。在这个过程中,第二个 IN
18、SERT 总会失败。下面看运行这个存储过程时会发生什么情况:ops$tkyteORA10G begin 2 p;3 end;4/I fired and updated 1 rows I fired and updated 1 rows begin*ERROR at line 1:ORA-02290:check constraint(OPS$TKYTE.SYS_C009598)violated ORA-06512:at OPS$TKYTE.P,line 5 ORA-06512:at line 2 ops$tkyteORA10G select*from t;no rows selected ops
19、$tkyteORA10G select*from t2;CNT-0 可以看到,Oracle 把这个存储过程调用处理为一个原子语句。客户提交了一个代码块 BEGIN P;END;,Oracle 在它外面包了一个SAVEPOINT。由于 P 失败了,Oracle 将数据库恢复到调用这个存储过程之前的时间点。下面,如果提交一个稍微不同的代码块,会得到完全不同的结果:ops$tkyteORA10G begin 2 p;3 exception 4 when others then null;5 end;6/I fired and updated 1 rows I fired and updated 1
20、rows PL/SQL procedure successfully completed.ops$tkyteORA10G select*from t;X-51 ops$tkyteORA10G select CNT-1 在此,我们运行的代码块会忽略所有错误,这两个代码块的输出结果有显著的差别。尽管前面第一个 P 调用没有带来任何改变,但在这里的 P 调用中,第一个 INSERT 会成功,而且 T2 中的 CNT 列会相应地递增。注意注意 如果代码中包含一个 WHEN OTHERS 异常处理器,但其中没有一个 RAISE 来重新引发异常,我认为这样的代码都是有 bug 的。它会悄悄地忽略错误,这就
21、改变了事务的语义。如果捕获 WHEN OTHERS,并把异常转换为旧式的返回码,这会改变数据库本该有的表现。Oracle 把客户提交的代码块认为是“语句”。这个语句之所以会成功,因为它自行捕获并忽略了错误,所以 If error then rollback没有起作用,而且执行这个语句后 Oracle 没有回滚到 SAVEPOINT。因此,这就保留了 P 完成的部分工作。为什么会保留这一部分工作呢?首要的原因是 P 中有语句级原子性:P 中的每条语句都具有原子性。P 提交其两条 INSERT 语句时就成为 Oracle 的客户。每个 INSERT 要么完全成功,要么完全失败。从以下事实就可以证明
22、这一点:可以看到,T 上的触发器触发了两次,而且将 T2 更新了两次,不过 T2 中的计数只反映了一个 UPDATE。P 中执行的第二个 INSERT 外包着一个隐式的 SAVEPOINT。以上两个代码块的差别很微妙,但是你在应用中必须考虑到这些问题。向一个 PL/SQL 块增加异常处理器可能会显著地改变它的行为。对此可以用另一种方式编写代码,将语句级原子性恢复为整个 PL/SQL 块级原子性,如下所示:ops$tkyteORA10G begin 2 savepoint sp;3 p;4 exception 5 when others then 6 rollback to sp;7 end;8
23、/I fired and updated 1 rows I fired and updated 1 rows PL/SQL procedure successfully completed.ops$tkyteORA10G select*from t;no rows selected ops$tkyteORA10G select*from t2;CNT-0 警告警告 前面的代码代表着一种极其糟糕的实践。一般来讲,不应该捕获 WHEN OTHERS,另外对于事务语义而言,也不应该为 Oracle已经提供的特性显式编写代码。在此模仿了 Oracle 通常用 SAVEPOINT 所做的工作,这样一来,
24、我们不仅仍然能捕获和“忽略”错误,还能恢复原来的行为。这个例子只作说明之用,这是一种非常糟糕的编码实践。8.2.3 事务级原子性 最后,还有一种事务级原子性的概念。事务(也就是一组 SQL 语句作为一个工作单元一同执行)的总目标是把数据库从一种一致状 6态转变为另一种一致状态。为了实现这个目标,事务也是原子性的,事务完成的所有工作要么完全提交并成为永久性的,要么会回滚并撤销。像语句一样,事务是一个原子性的工作单元。提交一个事务后,接收到数据库返回的“成功”信息后,你就能知道事务完成的所有工作都已经成为永久性的。8.3 完整性约束和事务 需要指出到底什么时候检查完整性约束。默认情况下,完整性约束
25、会在整个 SQL 语句得到处理之后才进行检查。也有一些可延迟的约束允许将完整性约束的验证延迟到应用请求时(发出一个 SET CONSTRAINTS ALL IMMEDIATE 命令)才完成,或者延迟到发出COMMIT 时再检查。8.3.1 IMMEDIATE 约束 在讨论的前一部分,我们假设约束都是 IMMEDIATE 模式,这也是一般情况。在这种情况下,完整性约束会在整个 SQL 语句得到处理之后立即检查。注意,这里我用的是“SQL 语句”而不只是“语句”。如果一个 PL/SQL 存储过程中有多条 SQL 语句,那么在每条 SQL 语句执行之后都会立即验证其完整性约束,而不是在这个存储过程完
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- oracle 专家高级编程 中文第八章 专家 高级 编程 中文 第八
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内