2007年6月22日星期五

《Expert one on one Oracle》- 事务 - 笔记

事务的四个属性ACID(Atomicity,Consistency,Isolation,Durability)。

1.1 事务控制语句

Oracle中没有“开始事务”的语句,当第一条更改数据的语句(第一个获取TX锁语句)执行的时候,便隐式地开始了一个事物。

可以通过COMMIT或者ROLLBACK来显式结束事务。否则,你使用的工具将会为你结束事务。例如在SQL*PLUS中,退出session时SQL*PLUS就会替你提交事务,而在PRO*C中会替你回滚事务。

相关控制语句语句有COMMIT,ROLLBACK,SAVEPOINT,ROLLBACK TO ,SET TRANSACTION。通过语句SET TRANSACTION可以指定事务的隔离级别,是否只读,以及使用的回滚段。

Oracle中事务具有Atomicity,语句也具有Atomicity。单条语句的失败,不会影响到前面已经执行语句的回滚。察看下面例子:

SQL> create table t( a int check(a>0 ) );
Table created.
SQL> insert into t values(1);
1 row created.
SQL> insert into t values(-1);
insert into t values(-1)
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.SYS_C003586) violated
SQL> select * from t;
A
----------
1
SQL>

上面例子,显示即使单条语句也具有原子性,第二条语句执行失败,但是第一条语句仍然成功。实际上Oracle处理上述语句的时候,隐式的按照如下方式执行:

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;

另为对于存储过程的调用Oracle也将其作为原子性语句来执行。

SQL> create table t( a int check(a>0 ) );
Table created.

SQL> create or replace procedure p1
2 as
3 begin
4 insert into t values (1);
5 insert into t values (-1);
6 end;
7 /
Procedure created.

SQL> begin p1; end;
2 /
begin p1; end;
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.SYS_C003588) violated
ORA-06512: at "SCOTT.P1", line 5
ORA-06512: at line 1

SQL> select * from t;
no rows selected

由于整个存储过程的调用作为一个原子性操作,所以第一条插入语句也被回滚。Oracle默认在调用存储过程之前插入了一个SAVEPOINT,所以P1调用失败,标回滚到调用P1之前,所以第一条插入语句也被回滚。

如果采用另外一段语句来调用P1:

SQL> begin p1;
2 exception
3 when others then null;
4 end;
5 /
PL/SQL procedure successfully completed.
SQL> select * from t;
A
----------
1

这是因为在调用过程中增加了异常处理函数。虽然捕获异常不做任何处理,Oracle还是认为执行成功了。这个时候,“如果错误,那么回滚”的原则在这里将不起作用。同时,在P1中,由于两条插入语句也是原子性操作,这个时候P1是提交这两条语句的Oracle客户端,所以第一条语句执行成功。

从上可以看到,在开发程序的过程中,对事物的控制是多么的重要。如果上述P1的例子的业务要求两次插入要么全部成功要么全部失败,增加异常处理语句而实际不做任何处理,将会带来与业务需求相悖的结果。

1.2 事务与完整约束性检查:

a. 是SQL语句的而不是语句的完整约束性检查。如果一个存储过程中包含多个SQL语句,那么每条SQL语句执行完成的时候,都需要检查。

b. 是在语句执行完成之后检查,而不是在执行过程中。如果更新10行纪录,那么是要等到10行记录更新之后才检查,而不是更新其中的某几条就检查。

c. 可以通过DEFERRED选项来延迟约束性检查。
DEFERRABLE INITIALLY DEFERRED:允许延迟验证约束,默认方式为延迟,等待提交后验证,可以修改为第二种状态。
DEFERRABLE INITIALLY IMMEDIATE: 允许延迟验证约束,默认方式为立即,处理完一条记录就立即验证,可以修改为第一种状态
NOT DEFERRABLE:不允许延迟验证约束,不能与上面两种状态进行转换。

SQL> create table pt
2 ( pk int primary key )
3 /
Table created.

SQL> create table ct
2 ( fk int constraint c_fk
3 references pt(pk)
4 deferrable
5 initially immediate -- 立即检查
6 )
7 /
Table created.

SQL> insert into pt values (1);
1 row created.

SQL> insert into ct values (1);
1 row created.

SQL> insert into ct values (2);--如果是立即检查,一旦不符合完整性约束,就会出错
insert into ct values (2)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.C_FK) violated - parent key not found

SQL> rollback;
Rollback complete.

SQL> insert into pt values (1);
1 row created.

SQL> set constraint c_fk deferred;--更换成提交的时候再检查
Constraint set.

SQL> insert into ct values (2);
1 row created.

SQL> commit;--直到提交的时候才检查
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02291: integrity constraint (SCOTT.C_FK) violated - parent key not found

1.3 编程的坏习惯

两个坏习惯
a. 可以一次提交的事务而多次提交。
b. 不设定足够大的回滚段。

关于第一点的测试,可以编写循环,在循环中多次提交,同一次提交所耗用的时间进行对比。

对于第二点的测试,可以将原来的rollback置为offline的状态,创建小的回滚段,执行循环更新的语句,检查结果是否报ORA‐01555错误。

对于ODBC和JDBC,默认都是自动提交。ODBC最初由SQL Server的开发人员设计。而在SQL Server中,锁是稀少资源,所以被设置为自动提交。而对于JDBC,在开发的时候最好将其设置为不自动提交。

1.4 分布式事务

Oracle能够透明的处理分布式事务,采用2PC协议来实现。通过DBLink,不同数据库之间可以相互访问。客户端最初登录的那个数据库,将作为事务的协调者。这样,这个数据库会询问其他数据库是否准备提交,其他的数据库就会反馈状态。如果都是YES,那么这个事务将会提交。如果有一个数据库反馈NO,那么整个事务就会回滚。

通常作为事务协调者的数据库如果出现故障,那么其他的数据库事务就会挂起等待。这个时候,需要手工在其他的数据库上进行提交或者回滚(因为有回滚段的存在)。

DBLink的使用有如下的限制:
a. 不能通过DB Link提交Commit语句。
b. 不能通过DB Link执行DDL语句。
c. 不能通过DB Link创建SAVEPOINT。

1.5 Redo和Undo
待续………….

没有评论: