大家都知道,回滚是数据库的一种能力,在语句或者事务发生错误,或者用户发出rollback语句的时候,将语句的影响或者整个事务的操作恢复到语句或事务执行之前的状态。也就是说,回滚使得语句和事务好像根本没有执行一样。
事实上真正回滚的只是用户发出的事务,而并非所有的一切都和事务开始前是一样的。
事务是所有关系型数据库的基本特性之一。所有的事务最终不外乎两种状态,提交和回滚。对于分布式事务,可能会产生第三种情况,也就是说,这个事务是悬而未决的。但是这种状态通过dba的手工干预,最终也是会变成提交或者回滚状态。
大家需要留意的是,oracle所进行的回滚仅仅是针对用户发出的事务而言,而很多的东西并不会随回滚的发生而回到原始的状态。比如包中的变量,它并不会随着事务的回滚而回复到事务开始之前的状态:
sql> create table t_rollback (id number);
表已创建。
sql> create or replace procedure p_test(p_out out number) as
2 begin
3 insert into t_rollback (id) values (1);
4 select count(*) into p_out from t_rollback;
5 end;
6 /
过程已创建。
sql> set serverout on
sql> declare
2 v_return number;
3 begin
4 p_test(v_return);
5 dbms_output.put_line(v_return);
6 rollback;
7 dbms_output.put_line(v_return);
8 end;
9 /
1
1
pl/sql 过程已成功完成。
|
对于oracle来说,回滚的对象是用户对数据的修改,而过程内部的变量并不会随着事务的回滚而恢复的初始状态。对于系统包中保存的变量的状态也是如此。
如果考虑自治事务,那么无法进行回滚的对象将更多:
sql> create or replace procedure p_test as
2 pragma autonomous_transaction;
3 begin
4 insert into t_rollback (id) values (1);
5 commit;
6 end;
7 /
过程已创建。
sql> select * from t_rollback;
未选定行
sql> exec p_test
pl/sql 过程已成功完成。
sql> insert into t_rollback (id) values (2);
已创建 1 行。
sql> select * from t_rollback;
id
----------
1
2
sql> rollback;
回退已完成。
sql> select * from t_rollback;
id
----------
1
|
除了用户自定义的自治事务无法被回滚,一些通过自治事务实现的功能,也是回滚无法恢复的。比如序列和审计。
sql> create sequence s_1;
序列已创建。
sql> select s_1.nextval from dual;
nextval
----------
1
sql> rollback;
回退已完成。
sql> select s_1.nextval from dual;
nextval
----------
2
|
回滚无法使序列的nextval值恢复到事务执行之前,同样的,即使dml被回滚,dml的审计也会记录下来:
sql> show parameter audit_trail
name type value
--------------- ----------- ------------------
audit_trail string db
sql> audit insert on t_rollback;
审计已成功。
sql> insert into t_rollback (id) values (3);
已创建 1 行。
sql> rollback;
回退已完成。
sql> col obj$name format a30
sql> select sessionid, userid, obj$name from sys.aud$;
sessionid userid obj$name
---------- ------------------------------
672 yangtk t_rollback
|
假如再考虑oracle本身的操作,那么回滚无法恢复原始状态的操作就太多了。比如dml语句产生的redo、undo;dml语句造成数据文件的修改;dml语句造成表和索引空间的扩展;语句的分析并cache在共享池;语句影响的block被cache在db_cache等等。即使将dml语句进行回滚,以上这些操作所产生的影响也已经存在了。