Oracle
笔记
-
自治事务
【
IT168
服务器学院】
第
15
章
自治事务
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION
;
15.1
为何使用自治事务
无法回滚的审计
一般情况下利用触发器禁止某些对表的更新等操作时,若记录日志,则触发器最后抛出异常时会造成日志回滚。利用自治事务可防止此点。
避免变异表
即在触发器中操作触发此触发器的表
在触发器中使用
DDL
写数据库
对数据库有写操作(
INSERT
、
UPDATE
、
DELETE
、
CREATE
、
ALTER
、
COMMIT
)的存储过程或函数是无法简单的用
SQL
来调用的,此时可以将其设为自治事务,从而避免
ORA-14552
(无法在一个查询或
DML
中执行
DDL
、
COMMIT
、
ROLLBACK
)、
ORA- 14551
(无法在一个查询中执行
DML
操作)等错误。需要注意的是函数必须有返回值,但仅有
IN
参数(不能有
OUT
或
IN/OUT
参数)。
开发更模块化的代码
在大型开发中,自治事务可以将代码更加模块化,失败或成功时不会影响调用者的其它操作,代价是调用者失去了对此模块的控制,并且模块内部无法引用调用者未提交的数据。
15.2
如何工作
事务控制
DECLARE
整个块都是属于父事务的,自治事务从离
PRAGMA
后的第一个
BEGIN
开始,只要此
BEGIN
块仍在作用域,则都属于自治事务。例如在
DECLARE
模块中声明一个写数据库的函数,则此函数虽然在自治事务所在存储过程执行,但其属于父事务;而自治事务中调用的任何函数和存储过程、激发的任何触发器等均为此自治事务的一部分。
自治事务可以嵌套,嵌套深度等只受
INIT.ORA
参数
TRANSACTIONS
(同时并发的事务数,缺省为
SESSIONS
的
1.1
倍)制约。
作用域
1.
包中的变量
自治事务可看到并修改父事务的变量,父事务也会察觉到这一改变,且不存在回滚问题。
2.
会话设置
/
参数
自治事务与父事务共享同一个会话环境,通过
ALTER SESSION
作的修改对整个会话均有效。但
SET TRANSACTION
是事务级的,仅对提起修改的事务有效。
3.
数据库修改
父事务已提交的修改对自治事务可见,未提交的对自治事务不可见,自治事务的修改对父事务是否可见取决于隔离级别(
Isolation Level
)。
对于游标,取决于其打开的位置,若其在父事务中打开,则之前父事务未提交的修改对其是有效的,在自治事务中这些修改也可见;而在自治事务中打开,则父事务未提交的修改不可见。
若使用缺省的
READ COMMITTED
隔离级别,则自治事务的修改对父事务可见;若改用
SERIALIZABLE
,则不可见。
4.
锁
父事务与自治事务是完全不同的事务,因此无法共享锁等。
结束一个自治事务
必须提交一个
COMMIT
、
ROLLBACK
或执行
DDL.
保存点
无法在自治事务中回滚到父事务中的一个保存点,只能在内部使用保存点。
15.3
最后说明
不支持分布式事务
截至
8.1.7
在自治事务中不支持分布式事务
仅可用
PL/SQL
全部事务回滚
若自治事务出错,则全部回滚,即便父事务有异常处理模块。
事务级临时表
每个会话仅一个事务可访问事务级临时表(多个会话中的事务可并发操作)。
变异表
15.4
可能遇到的错误
ORA-06519
–
检查到活动自治事务,回滚——退出自治事务时没有提交、回滚或
DDL
操作
ORA-14450
–
试图访问正在使用的事务级临时表
ORA-00060
–
等待资源时检查到死锁
Oracle
中使用自治事务保存日志表条目
数据库事务是一种单元操作,要么是全部操作都成功,要么全部失败。在Oracle中,一个事务是从执行第一个数据管理语言(DML)语句开始,直到执行一个COMMIT语句,提交保存这个事务,或者执行一个ROLLBACK语句,放弃此次操作结束。
事务的“要么全部完成,要么什么都没完成”的本性会使将错误信息记入数据库表中变得很困难,因为当事务失败重新运行时,用来编写日志条目的INSERT语句还未完成。
针对这种困境,Oracle提供了一种便捷的方法,即自治事务。自治事务从当前事务开始,在其自身的语境中执行。它们能独立地被提交或重新运行,而不影响正在运行的事务。正因为这样,它们成了编写错误日志表格的理想形式。在事务中检测到错误时,您可以在错误日志表格中插入一行并提交它,然后在不丢失这次插入的情况下回滚主事务。
因为自治事务是与主事务相分离的,所以它不能检测到被修改过的行的当前状态。这就好像在主事务提交之前,它们一直处于单独的会话里,对自治事务来说,它们是不可用的。然而,反过来情况就不同了:主事务能够检测到已经执行过的自治事务的结果。
要创建一个自治事务,您必须在匿名块的最高层或者存储过程、函数、数据包或触发的定义部分中,使用PL/SQL中的PRAGMA AUTONOMOUS_TRANSACTION语句。在这样的模块或过程中执行的SQL Server语句都是自治的。
触发无法包含COMMIT语句,除非有PRAGMA AUTONOMOUS_TRANSACTION标记。但是,只有触发中的语句才能被提交,主事务则不行。
列表A展示了对一个简单但灵活的错误日志表格所作的CREATE TABLE和CREATE SEQUENCE语句。
CREATE TABLE errorlog ( errorlog_id NUMBER, logged_on TIMESTAMP DEFAULT SYSTIMESTAMP, logged_by VARCHAR2(30) DEFAULT USER, num1 NUMBER, num2 NUMBER, num3 NUMBER, text1 VARCHAR2(1000), text2 VARCHAR2(1000), text3 VARCHAR2(1000) ); CREATE SEQUENCE errorlog_seq START WITH 1 INCREMENT BY 1; |
列表B是一个独立的存储过程,用于更新错误日志表格。
CREATE OR REPLACE PROCEDURE log_error ( n1 IN NUMBER:=NULL, t1 IN VARCHAR:=NULL, n2 IN NUMBER:=NULL, t2 IN VARCHAR:=NULL, n3 IN NUMBER:=NULL, t3 IN VARCHAR:=NULL ) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO errorlog (errorlog_id, num1, num2, num3, text1, text2, text3) VALUES (errorlog_seq.NEXTVAL, n1, n2, n3, t1, t2, t3); COMMIT; END; |
该过程接受最多三个数字和三个文本变量,然后将它们和时间戳以及调用过程的用户一起储存在表格中。
要测试这个过程,就要更新(UPDATE)或删除(DELETE)表格中的某些行;这就引发了主事务。然后执行存储过程,将您选择的要记入日志的数据传递给它。最后,重新运行主事务,选择(SELECT)错误日志表格,您的日志条目仍会在那儿。
(T114)