某客户上个月刚迁移升级的业务系统在今天执行某条sql查询报ORA-19706:invalid
SCN,导致无法正常显示查询结果。经了解,具体报错的基本情况是:
192.168.2.85上的用户通过db_link去访问192.168.2.45下面的数据,报:

我们统计了下午3点多到晚上10点多的SCN的增长速度
|
下午3点多的SCN值
|
晚上10点多的SCN
|
相差
|
192.168.2.45(B库)
|
14246110914784
|
14245785946041
|
324968734
|
192.168.2.85(A库)
|
14238384967631
|
14238383909345
|
1058286
|
看一下ORA-19706错误的解释:可以看到too
large是产生该问题的原因之一
$oerr
ora 19706
19706,
00000, "invalid SCN"
//
*Cause: The input SCN is either not
a positive integer or too large.
//
*Action: Check the input SCN and make sure it is a valid
SCN.
|
我们先来了解下SCN
1:
SCN
SCN是当Oracle数据库更新后,由DBMS自动维护去累积递增的一个数字。
在Oracle内部,SCN分为两部分存储,分别称之为scn
wrap和scn
base。SCN长度为48位,即它其实就是一个48位的整数。那么SCN这个48位长的整数,最大就是 2^48(2的48次方,
281万亿,281474976710656),这是很大的一个数字了。
据推算这个值可以保证Oracle数据库理论上可以处理500年的数据。
Maximum
Reasonable
SCN:在当前时间点,SCN最大允许达到的SCN值被称为" Maximum
Reasonable SCN“(最大合理SCN),也称为Reasonable SCN
Limit,简称RSL。这个值是一个限制,避免数据库的SCN无限制地增大,甚至达到了SCN的最大值。该值可以通过如下方式计算:
col
scn for 999,999,999,999,999,999
select
(
(
(
(
(
(
to_char(sysdate,'YYYY')-1988
)*12+
to_char(sysdate,'mm')-1
)*31+to_char(sysdate,'dd')-1
)*24+to_char(sysdate,'hh24')
)*60+to_char(sysdate,'mi')
)*60+to_char(sysdate,'ss')
)
* to_number('ffff','XXXXXXXX')/4 scn
from
dual
/
|
SCN
Headroom:这个是指Maximum
Reasonable
SCN与当前数据库SCN的差值。在alert中通常是以“天”为单位,这个只是为了容易让人读而已。天数=(Maximum
Reasonable SCN-Current
SCN)/16384/3600/24。这个值就的意思就是,如果按SCN的每大增长速率,多少天会到达Maximum
Reasonable SCN。但实际上即使如此,也不会到达Maximum Reasonable
SCN,因为到那时Maximum Reasonable SCN也增大了(越时间增大),要到达Maximum
Reasonable SCN,得必须以SCN最大可能速率的2倍才行。
关于SCN
Headroom, Oracle提供了一个脚本 scnhealthcheck.sql 用于检查数据库当前SCN的剩余情况
以下是这个脚本的内容:
Rem
Rem
$Header: rdbms/admin/scnhealthcheck.sql st_server_tbhukya_bug-13498243/8
2012/01/17 03:37:18 tbhukya Exp $
Rem
Rem scnhealthcheck.sql
Rem
Rem
Copyright (c) 2012, Oracle and/or its affiliates. All rights
reserved.
Rem
Rem
NAME
Rem scnhealthcheck.sql - Scn Health
check
Rem
Rem
DESCRIPTION
Rem Checks scn health of a
DB
Rem
Rem NOTES
Rem
.
Rem
Rem MODIFIED
(MM/DD/YY)
Rem tbhukya 01/11/12 -
Created
Rem
Rem
define LOWTHRESHOLD=10
define
MIDTHRESHOLD=62
define VERBOSE=TRUE
set veri off;
set feedback
off;
set serverout on
DECLARE
verbose
boolean:=&&VERBOSE;
BEGIN
For C in (
select
version,
date_time,
dbms_flashback.get_system_change_number current_scn,
indicator
from
(
select
version,
to_char(SYSDATE,'YYYY/MM/DD HH24:MI:SS')
DATE_TIME,
((((
((to_number(to_char(sysdate,'YYYY'))-1988)*12*31*24*60*60)
+
((to_number(to_char(sysdate,'MM'))-1)*31*24*60*60)
+
(((to_number(to_char(sysdate,'DD'))-1))*24*60*60)
+
(to_number(to_char(sysdate,'HH24'))*60*60)
+
(to_number(to_char(sysdate,'MI'))*60)
+
(to_number(to_char(sysdate,'SS')))
) * (16*1024)) - dbms_flashback.get_system_change_number)
/
(16*1024*60*60*24)
) indicator
from
v$instance
)
) LOOP
dbms_output.put_line(
'-----------------------------------------------------'
|| '---------' );
dbms_output.put_line( 'ScnHealthCheck' );
dbms_output.put_line(
'-----------------------------------------------------'
|| '---------' );
dbms_output.put_line( 'Current Date: '||C.date_time
);
dbms_output.put_line( 'Current SCN: '||C.current_scn
);
if (verbose) then
dbms_output.put_line( 'SCN
Headroom: '||round(C.indicator,2) );
end if;
dbms_output.put_line( 'Version: '||C.version
);
dbms_output.put_line(
'-----------------------------------------------------'
|| '---------' );
IF C.version > '10.2.0.5.0'
and
C.version NOT LIKE '9.2%'
THEN
IF C.indicator>&MIDTHRESHOLD
THEN
dbms_output.put_line('Result: A - SCN
Headroom is good');
dbms_output.put_line('Apply the latest recommended
patches');
dbms_output.put_line('based on your
maintenance schedule');
IF (C.version <
'11.2.0.2') THEN
dbms_output.put_line('AND set
_external_scn_rejection_threshold_hours='
|| '24 after apply.');
END
IF;
ELSIF C.indicator<=&LOWTHRESHOLD
THEN
dbms_output.put_line('Result: C - SCN
Headroom is low');
dbms_output.put_line('If
you have not already done so apply' );
dbms_output.put_line('the latest recommended patches right now'
);
IF (C.version < '11.2.0.2')
THEN
dbms_output.put_line('set
_external_scn_rejection_threshold_hours=24
'
|| 'after apply');
END
IF;
dbms_output.put_line('AND contact Oracle
support immediately.' );
ELSE
dbms_output.put_line('Result: B - SCN
Headroom is low');
dbms_output.put_line('If
you have not already done so apply' );
dbms_output.put_line('the latest recommended patches right
now');
IF (C.version < '11.2.0.2')
THEN
dbms_output.put_line('AND set
_external_scn_rejection_threshold_hours='
||'24 after apply.');
END
IF;
END IF;
ELSE
IF
C.indicator<=&MIDTHRESHOLD THEN
dbms_output.put_line('Result: C - SCN Headroom is
low');
dbms_output.put_line('If you have not
already done so apply' );
dbms_output.put_line('the latest recommended patches right now'
);
IF (C.version >= '10.1.0.5.0'
and
C.version <=
'10.2.0.5.0' and
C.version NOT LIKE '9.2%') THEN
dbms_output.put_line(', set
_external_scn_rejection_threshold_hours=24'
|| ' after apply');
END
IF;
dbms_output.put_line('AND contact Oracle
support immediately.' );
ELSE
dbms_output.put_line('Result: A - SCN
Headroom is good');
dbms_output.put_line('Apply the latest recommended
patches');
dbms_output.put_line('based on your
maintenance schedule ');
IF (C.version >=
'10.1.0.5.0' and
C.version <= '10.2.0.5.0'
and
C.version NOT LIKE
'9.2%') THEN
dbms_output.put_line('AND
set
_external_scn_rejection_threshold_hours=24'
|| ' after apply.');
END
IF;
END IF;
END IF;
dbms_output.put_line(
'For further information review MOS
document id 1393363.1');
dbms_output.put_line(
'-----------------------------------------------------'
|| '---------' );
END
LOOP;
end;
/
|

以下是客户实际环境检查输出结果:
SCN的异常增长几种类型
1:数据库内部BUG触发,
2:人为调整导致SCN异常增长过大,比如数据库通过特殊手段强制打开,手工把SCN递增得很大。
3:通过db
link传播。如果A库通过db
link连接到B库,如果A库的SCN高于B库的SCN,那么B库就会递增SCN到跟A库一样,反之如果A库的SCN低于B库的SCN,那么A库的SCN 会递增到跟B库的SCN一样。也就是说,涉及到db
link进行操作的多个库,它们会将SCN同步到这些库中的最大的SCN。
从客户场景的实际情况来看,属于第3种类型导致SCN异常增长。B库的SCN值要高于A库的SCN,因此要将B库的SCN增同步到A库,但是如果B库的SCN过高,这样同步到A库之后,
使得A库面临Headroom过小的风险,那么A库会拒绝同步SCN,这个时候就会报ORA-19706:
Invalid SCN错误。
解决方法:
1:由于B库数据库版本为10.2.0.4.
Oracle推荐解决SCN增长异常的最佳办法就是给B库打上相应的补丁或者直接升级到10205并打上最新的PSU
.但由于B库和A库不是同一个中心管辖的系统,而且B库也不允许升级带来的风险和停机时间。
2:只能委曲求全从A库下手,由于A库是最新升级的oracle
11203版本,通过隐含参数_external_scn_rejection_threshold_hours来处理这个问题,
在11203版本中,该值默认为24,单位小时。从_external_scn_rejection_threshold_hours这个参数名的字面意思结合它的作用,可以说这个参数就是”拒绝外部SCN“的阈值。极端情况下可以设置为1。
在本案例中,减少该隐含参数的值,我们将该值设置为1,重启数据库后,ORA-19706报错得以解决。