Oracle数据库在安装了2012年1月发布的CPU或PSU补丁之后,经常出现下面一些现象:

  • 应用出现ORA-19706: invalid SCN错误。
  • 在alert日志中出现类似于:
    Wed May 30 15:09:57 2012
    Advanced SCN by 68093 minutes worth to 0x0ba9.4111a520, by distributed transaction remote logon, remote DB:xxxx.
    Client info : DB logon user xxxx, machine xxxx, program oracle@xxxx (J001), and OS user oracle
    这样的警告。
  • 在alert日志中出现类似于:
    Wed May 30 12:02:00 2012
    Rejected the attempt to advance SCN over limit by 166 hours worth to 0x0ba9.3caec689, by distributed transaction remote logon, remote DB: xxxx.
    Client info : DB logon user xxxx, machine xxxx, program oracle@xxxx (J000), and OS user oracle
    这样的错误信息。
  • 在alert日志中出现类似于:
    Sat Mar 17 05:57:45 2012
    ALTER DATABASE OPEN
    ************************************************************
    Warning: The SCN headroom for this database is only 38 days!
    ************************************************************
    这样的信息。
  • 在MOS文档《ORA-19706 and Related Alert Log Messages [ID 1393360.1]》中还提到其他会出现在alert中的一些警告信息:
    Warning - High Database SCN: Current SCN value is 0x0b7b.0008e40b, threshold SCN value is 0x0b75.055dc000, If you have not previously reported this warning on this database, please notify Oracle Support so that additional diagnosis can be performed.
    WARNING: This patch can not take full effect until this RAC database has been completely shutdown and restarted again.Oracle recommends that it is done at the earliest convenience.

如果说以上的现象只是警告或应用级报错,影响范围有限,那么不幸的是如果遇到RECO进程在恢复分布式事务时遇到SCN问题,则可能使数据库宕掉,例如:


Wed May 30 14:44:02 2012
Errors in file /oracle/admin/miboss/bdump/xxxx_reco_225864.trc:
ORA-19706: invalid SCN
Wed May 30 14:44:02 2012
Errors in file /oracle/admin/miboss/bdump/xxxx_reco_225864.trc:
ORA-00600: internal error code, arguments: [18348], [0x000000000], [485331304561], [], [], [], [], []
.........
RECO: terminating instance due to error 476
Intance terminated by RECO, pid s= 225864

那么2012年1月发布的CPU或PSU补丁到底使数据库在SCN处理方面产生了什么样的变化?这种变化对数据库有什么危害吗?甚至于说,以上提示的信息是由于这个补丁的BUG引起的吗?

要回答这些问题,得先从SCN讲起。SCN可以说是Oracle中的很基础,但同时也是很重要的东西,它是一个单向增长的“时钟”,广泛应用于数据库的恢复、事务ACID、一致性读还有分布式事务中。那么除了这些,SCN还有以下一些知识点:

  • SCN的内部存储方式:在Oracle内部,SCN分为两部分存储,分别称之为scn wrap和scn base。实际上SCN长度为48位,即它其实就是一个48位的整数。只不过可能是由于在早些年通常只能处理32位甚至是16位的数据,所以人为地分成了低32位(scn base)和高16位(scn wrap)。为什么不设计成64位,这个或许是觉得48位已经足够长了并且为了节省两个字节的空间:)。那么SCN这个48位长的整数,最大就是2^48(2的48次方, 281万亿,281474976710656),很大的一个数字了。
  • Maximum Reasonable SCN:在当前时间点,SCN最大允许达到(或者说最大可能)的SCN值。也称为Reasonable SCN Limit,简称RSL。这个值是一个限制,避免数据库的SCN无限制地增大,甚至达到了SCN的最大值。这个值大约是这样一个公式计算出来的:(当前时间-1988年1月1日)*24*3600*SCN每秒最大可能增长速率。当前时间减1988年1月1日的结果是天数,24表示1天24小时,3600表示1小时3600秒。不过这个公式里面“当前时间-1988年1月”部分并不是两个时间直接相减,而是按每月31天进行计算的(或许是为了计算简单,因此在Oracle内部可能要频繁地计算,这个计算方法可以在安装了13498243这个补丁后得到的scnhealthcheck.sql文件中看到,《Installing, Executing and Interpreting output from the "scnhealthcheck.sql" script [ID 1393363.1]》这篇MOS文档解释了这个脚本的使用及对结果的解释,实际上直接看脚本代码更为清楚)。那么SCN最秒最大可能增长速率是多少呢,这个跟Oracle版本有一定的关系,在11.2.0.2之前是16384(即16K),在11.2.0.2及之后版本是32768(即32K)。在11.2.0.2的版本中有一个隐含参数,_max_reasonable_scn_rate,其默认值就是32768(不建议调整这个值)。如果按16K的最大值,SCN要增长到最大,要超过500年。
  • 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的异常增长:通常来说,每秒最大允许的16K/32K增长速率已经足够了,但是不排除由于BUG,或者人为调整导致SCN异常增长过大。特别是后者,比如数据库通过特殊手段强制打开,手工把SCN递增得很大。同时Oracle的SCN会通过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。
  • 那么,如果是数据库本身操作而不是通过db link同步使得SCN的增长,其增长速率如何判断呢,这个可以通过系统的统计量“calls to kcmgas”和"DEBUG calls to kcmgas"来得到。kcmgas的意思是get and advance SCN,即获取并递增SCN。
  • 在两个库通过db link进行分布式事务时,假设B库的SCN值要高于A库的SCN,因此要将B库的SCN增同步到A库,但是如果B库的SCN过高,这样同步到A库之后,使得A库面临Headroom过小的风险,那么A库会拒绝同步SCN,这个时候就会报ORA-19706: Invalid SCN错误。分布式事务,或者说是通过db link的操作就会失败,即使是通过db link的查询操作。这里显然有一个阈值,如果递增SCN使得Headroom过小到什么值时,就会拒绝递增(同步)SCN?目前来看是这样:如果打了2012年1月CPU或PSU补丁,11.2.0.2及以后的版本,是1天即24小时,其他版本是31天即744小时,打了补丁之后可以由隐含参数_external_scn_rejection_threshold_hours来调整。而没有打补丁的情况下,视同此参数设为0,实际最小为1小时。由于Oracle 9.2.0.8没有了最新的补丁集,显示也不会有这个参数,保持默认为1小时。注意这是一个静态参数。所以打了2012年1月CPU或PSU补丁的一个重要变化是增加了_external_scn_rejection_threshold_hours参数,同时使11.2.0.2以下版本的数据库其Headroom的阈值增得较大。这带来的影响就是ORA-19706的错误出现的概率更高。解决的办法将_external_scn_rejection_threshold_hours这个隐含参数设置为较小的值,推荐的值是24,即1天。从_external_scn_rejection_threshold_hours这个参数名的字面意思结合它的作用,可以说这个参数就是”拒绝外部SCN“的阈值。对于数据库自身产生的SCN递增是没有影响的。
  • 虽然11.2.0.2及之后的版本,其默认的每秒最大可能SCN增长速率为32K,这使得Maximum Reasonable SCN更大,也就是说其SCN可以增长到更大的值。那也就是可能会使11.2.0.2的库与低版本的数据库之间不能进行db link连接。或者是11.2.0.2的库不能与16K速率的(比如调整了_max_reasonable_scn_rate参数值)的11.2.0.2的库进行db link连接。

现在是时候来回答以下几个问题了:

  • 2012年1月后发布的CPU或PSU补丁到底使数据库在SCN处理方面产生了什么样的变化?答案是:增加了_external_scn_rejection_threshold_hours参数,11.2.0.2及以上版本的这个参数默认值是24,其他版本默认值是744。这样使11.2.0.2以下版本的数据库其Headroom的阈值增得较大。
  • 这种变化对数据库有什么危害吗?答案是:在一个具有很多系统的大型企业环境里面,db link使用很多,甚至有一些不容易管控到的数据库也在跟关键系统通过 db link进行连接,在这样的环境中,过高的SCN扩散到关键系统,而系统如果打了这个补丁,其Headroom阈值变大,那么就更容易出现ORA-19706错误,对db link依赖很严重的系统可能会导致业务系统问题,严重情况下甚至会宕库。不过通过设置隐含参数_external_scn_rejection_threshold_hours可解决这样的问题。所以,如果你安装了2012年1月的CPU或PSU补丁,请尽快设置此参数为建议的值24,极端情况下你可以设置为1。
  • alert中的那些提示或警告信息是BUG引起的吗?答案是:这些提示或警告不是BUG引起的。它只是提醒你注意SCN过高增长,或者是你的Headroom较小(在Headroom小于62天时可能会提醒),引起你的重视。实际上根据MOS文档《System Change Number (SCN), Headroom, Security and Patch Information [ID 1376995.1]》的说法,这个补丁修复了SCN相关的一些BUG。如果非要说BUG,可以勉强认为补丁安装后新增的参数_external_scn_rejection_threshold_hours其默认值过大。Bug 13554409 - Fix for bug 13554409 [ID 13554409.8]就是说的这个问题。不过这个问题已经在2012年4月的CPU或PSU补丁中得到修复。

在最后我们来解读一下alert日志中的一些信息:

  • 信息:
    Wed May 30 15:09:53 2012
    Completed crash recovery at
    Thread 1: logseq 3059, block 19516, scn 12754630269552
    2120 data blocks read, 2120 data blocks written, 19513 redo blocks read
    .....
    Wed May 30 15:09:57 2012
    Advanced SCN by 68093 minutes worth to 0x0ba9.4111a520, by distributed transaction remote logon, remote DB:xxxx.
    Client info : DB logon user xxxx, machine xxxx, program oracle@xxxx (J001), and OS user oracle
    这里是说,SCN向前(跳跃)递增了68098分钟,其递增后的SCN是0x0ba9.4111a520。注意这里的分钟的计算就是根据SCN每秒最大可能增长速率为16K来的。我们计算一下:
    0x0ba94111a520转换成10进制12821569053984。
    在alert日志中,这个信息是刚打开数据库的时候,所以 crash recovery完成时的scn可以做为近似的当前SCN,其值为12754630269552:
    (12821569053984-12754630269552)/16384/60=68093.65278320313
    这里16384值的是SCN每秒最大可能增长速率,可以看到计算结果极为接近。

    我们再来计算一下这个SCN的headroom是多少:

    SQL>    select
      2     ((((
      3      ((to_number(to_char(cur_date,'YYYY'))-1988)*12*31*24*60*60) +
      4      ((to_number(to_char(cur_date,'MM'))-1)*31*24*60*60) +
      5      (((to_number(to_char(cur_date,'DD'))-1))*24*60*60) +
      6      (to_number(to_char(cur_date,'HH24'))*60*60) +
      7      (to_number(to_char(cur_date,'MI'))*60) +
      8      (to_number(to_char(cur_date,'SS')))
      9      ) * (16*1024)) - 12821569053984)
     10     / (16*1024*60*60*24)
     11     ) headroom
     12     from (select to_date('2012-05-30 15:09:57','yyyy-mm-dd hh24:mi:ss') cur_date from dual);
    
      HEADROOM
    ----------
    24.1496113
    

    可以看到结果为24天,由于这个时候_external_scn_rejection_threshold_hours参数值为24,即1天,所以虽然有这么大的跳跃,但SCN仍然增长成功。

  • 信息:
    Wed May 30 12:02:00 2012
    Rejected the attempt to advance SCN over limit by 166 hours worth to 0x0ba9.3caec689, by distributed transaction remote logon, remote DB: xxxx.
    Client info : DB logon user xxxx, machine xxxx, program oracle@xxxx (J000), and OS user oracle
    在这个信息中,拒绝了db link引起的SCN增加。计算一下这个SCN的headroom:
    0x0ba93caec689转换成10进制是12821495465609
    当前时间是2012-05-30 12:02:00,

    SQL>    select
      2     ((((
      3      ((to_number(to_char(cur_date,'YYYY'))-1988)*12*31*24*60*60) +
      4      ((to_number(to_char(cur_date,'MM'))-1)*31*24*60*60) +
      5      (((to_number(to_char(cur_date,'DD'))-1))*24*60*60) +
      6      (to_number(to_char(cur_date,'HH24'))*60*60) +
      7      (to_number(to_char(cur_date,'MI'))*60) +
      8      (to_number(to_char(cur_date,'SS')))
      9      ) * (16*1024)) - 12821495465609)
     10     / (16*1024*60*60*24)
     11     ) headroom
     12     from (select to_date('2012-05-30 12:02:00','yyyy-mm-dd hh24:mi:ss') cur_date from dual);
    
      HEADROOM
    ----------
    24.0710752
    

    由于这个时候_external_scn_rejection_threshold_hours参数值为744,即31天,计算出的headroom在这个阈值之内,因此拒绝增加SCN。
    (31-24.0710752)*24=166.2941952,正好是166小时。

--update on 2012/6/2--
实际上2012年1月的CPU或PSU补丁之后还会有下面的变化:

  1. _minimum_giga_scn这个隐含没有了,可惜了这个手工增加SCN的利器。
  2. 11.2.0.2及之后的版本,从原来的32K SCN最大速率调整回了16K速率。可以用下面的SQL来得到结果:
    SQL&gt select decode(bitand(DI2FLAG,65536),65536,'Y','N') using16 
      2   from x$kccdi2;
    
    U
    -
    Y
    

    上面的SQL的结果只有在11.2.0.2及以上版本才有意义,结果为Y,表示使用的是16K的速率,否则是使用32K速率。

本文涉及的一些参数,和SCN的一些算法,可能会随着版本或补丁的变化而产生较大的变化。

important update: 实际上在Jan 2012的PSU/CPU补丁中存在较大的SCN BUG,目前已经不建议打这个补丁集,而是打到更高的PSU补丁集上。

Trackback

10 comments untill now

  1. 使得A库面临Headroom过小的风险,那么A库会拒绝同步SCN.
    是不是打2012年1月CPU或PSU后才有的限制?我发现没有打补丁的库没有隐含参数_external_scn_rejection_threshold_hours。10.2.0.4.0 patch后默认是24。
    几个问题:
    ORA-19706的root cause是不是打上了这个补丁?
    但ORA-19706并不是这个补丁的bug,而是oracle认为SCN的增长速度不合理?

    10.2.0.4打了补丁之后还是没有_max_reasonable_scn_rate这个参数,即使最大SCN合理增速无法调整

    [回复]

    老熊 回复:

    @xqka, _max_reasonable_scn_rate这个参数并不是这个补丁带来的,是11.2.0.2及以上版本固有的。

    ORA-19706的root cause是不是打上了这个补丁?root cause应该还是说数据库的SCN过高,只不过打了这个补丁后,默认情况下对Headroom要求更严了。

    所以如果打了这个补丁,需要把_external_scn_rejection_threshold_hours改为24。

    [回复]

  2. 打上这个补丁之后headroom不可能为负值,即使current scn不可能大于reasonable scn。宁愿报错也不愿使得current scn过大,对不?

    但是在频繁使用dblink的环境scn增速大于16K有时候是难于避免的。例如A、B、C三个库,两两dblink互联,A是早上忙,B是下午忙,C是晚上忙,那么在scn增速看来,它们全天都很忙。scn增速很可能大于16K,这种情况下打这个补丁是不是成为一种风险?注意:10.2.0.4没找到改变scn合理增速的方法。

    [回复]

    老熊 回复:

    @xqka, 每秒增速大于16K是很少的,就算是高峰期,除非是一些BUG。通常提交一次事务才会增大SCN,那很显然不太可能每秒有那么高的事务量。

    [回复]

  3. reasonable scn 计算(16k速率)
    select to_number(months_between(sysdate,to_date(‘1988-01-01′,’YYYY-MM-DD’)))*31*24*60*60*16*1024 as scn from dual;

    现在正遇到这个麻烦事,
    继续关注,谢谢老熊!

    [回复]

  4. 我在eygle里面看到说:Oracle在内部控制每秒增减的SCN不超过 16K,按照这样计算,这个数值可以使用大约544年;请问会有些什么原因导致SCN达到最大值呢?我要怎么判断我的scn是不是在正常的增加呢?

    [回复]

    老熊 回复:

    @ricky, 这两个问题在文章中都有提及,请参考一下这篇文章。

    [回复]

  5. 今天数据库宕机,与你说的有点象。
    在警告日志中首先出现下面错误:
    ORA-02050: transaction 25.1.54071 rolled back, some remote DBs may be in-doubt
    查了资料,这个错误与分布式处理有关
    接下来的异常是:
    Fri Aug 10 09:01:17 EAT 2012
    Errors in file /oracle/app/oracle/admin/kfqadb/bdump/kfqadb_smon_29327.trc:
    ORA-00600: internal error code, arguments: [1433], [60], [], [], [], [], [], []
    这应该是宕机时的异常,
    重启数据库后出现下面提示:
    Warning: The SCN headroom for this database is only 15 days!
    虽然与你举的‘如果遇到RECO进程在恢复分布式事务时遇到SCN问题,则可能使数据库宕掉’例子错误信息不一样,但感觉诱因是一样的。
    这样判断是否正确,请大师指点。谢谢!

    [回复]

  6. 请教个问题,
    select max(ksppstvl) scn_incr_value from sys.x$ksppi a, sys.x$ksppcv b
    where a.ksppinm =’_max_reasonable_scn_rate’ and b.indx=a.indx

    SCN_INCR_VALUE
    ————————————-
    32768

    select decode(bitand(DI2FLAG,65536),65536,’Y’,’N’) using16 from x$kccdi2;

    USING16
    ——-
    Y

    这种情况是以第二个查询结果为准吗?

    [回复]

  7. 大师的文章就是不一样,着实深入。

    [回复]

Add your comment now