前两篇文章(TAF PartITAF PartII)主要描述了在TAF发生故障转移时正在执行SELECT时的行为。本文将观察当故障转移发生时如果正在执行DML和DDL语句的行为。

本文将继续使用上一篇文章TAF PartII同样的测试环境。

我们先测试当会话update时,在另一个会话立即start force实例:

SQL> update t1 set object_name=lpad('x',40,'x'),created=sysdate;
update t1 set object_name=lpad('x',40,'x'),created=sysdate
*
ERROR 位于第 1 行:
ORA-25408: 无法安全重放调用

SQL> select * from dual;

D
-
X

SQL> select failed_over from v$session where sid=(select sid from v$mystat where rownum=1);

FAI
---
YES

可以看到,在数据库实例重启完成后,会话报“ORA-25408: 无法安全重放调用”错误。为什么会报这个错。我的猜测是,由于在FAILOVER后,会话实际上是重新来执行这个SQL,但是DML语句不像SELECT一样,前者是对当前时间点的数据进行修改,保证数据修改是最新的;而SELECT的机制是保证查询的时间点一致,DML语句在重新执行时,当前时间点的数据已经跟上次执行时的数据可能不相同了,不能保证是相同的,也就是“不安全”的。

从上面的输出可以看到,在会话报错之后,仍然可以执行查询,并且从结果来看表明会话是failover后的新会话。注意,ORA-25408这个错误是在failover之后才报的,并不是数据库实例DOWN下后马上就报错。

如果我们在数据库实例级别开启sql trace,在failover后我们可以从trace文件里面可以发现,UPDATE语句并没有再次被解析和执行,而不是在UPDATE重新执行然后报错。

下面我们来看看DDL语句的情况:

SQL> create index t1_idx on t1(object_id,object_name,object_type,created);
create index t1_idx on t1(object_id,object_name,object_type,created)
*
ERROR 位于第 1 行:
ORA-25408: 无法安全重放调用

SQL> select failed_over from v$session where sid=(select sid from v$mystat where rownum=1);

FAI
---
YES

可以看到,整个过程和报错都跟DML语句完全一样的。

再来看看下面的一种情况,如果一个DML已经执行完,但是还没有提交,会发生什么?

首先我们执行UPDATE,但不提交

SQL> update t1 set object_name=lpad('x',40,'x') where rownum<=10;

已更新10行。

在另一个会话中重启数据库:

SQL> startup force;
ORA-32004: obsolete and/or deprecated parameter(s) specified
ORACLE instance started.

Total System Global Area  167772160 bytes
Fixed Size                  1266392 bytes
Variable Size              92278056 bytes
Database Buffers           71303168 bytes
Redo Buffers                2924544 bytes
Database mounted.
Database opened.

最后,在执行UPDATE的那个会话执行SQL语句:

SQL> select * from dual;
select * from dual
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

错误出现了,“ORA-25402: 事务处理必须重新运行”。看起来如果会话在事务中,SELECT语句也会失败。继续测试其他的SQL:

SQL> update t1 set object_name=lpad('y',40,'y') where rownum<=10;
update t1 set object_name=lpad('y',40,'y') where rownum<=10
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

SQL> commit;
commit
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

SQL> select failed_over from v$session where sid=(select sid from v$mystat where rownum=1);
select failed_over from v$session where sid=(select sid from v$mystat where rownum=1)
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

SQL> create index t1_idx on t1(object_id,object_name,object_type,created);
create index t1_idx on t1(object_id,object_name,object_type,created)
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

SQL> alter session set nls_date_format='yyyy-mm-dd';
alter session set nls_date_format='yyyy-mm-dd'
*
ERROR 位于第 1 行:
ORA-25402: 事务处理必须重新运行

SQL> rollback;

回退已完成。

看起来,除了rollback语句之外,不能执行其他任何SQL语句。从sql trace的trace文件里面可以发现,这些SQL并没有任何记录。

rollback之后,就可以继续执行任意类型的SQL语句了。

最后的一个测试表明,如果一个会话正在事务中,那么failover后,只有回滚之后才能执行SQL语句。如果一个应用遭遇到failover后频繁地报“ORA-25402”错误,则说明应用没有能够正确地处理事务异常。这种情况是很少的,大多数的应用都会用异常处理机制来保护整个事务,一旦出现异常就立即回滚事务。

Trackback

no comment untill now

Add your comment now