在编译《Oracle Core——Essential Internals for DBAs and Developers》这本书的第6章时,这章有提到进程在查找空闲缓冲区时,会从REPL_AUX链(即辅助LRU链)开始扫描,在扫描的过程中发现有dirty buffer,则会将该buffer从REPL_AUX链取下再链到WRITE_MAIN链上。这里提到的REPL_AUX链,主要用于链接那些能够马上复用的buffer(缓冲区),比如一致性读块,很少访问的块,大表全表扫描的块。而进程在查找可用的空闲或可复用的缓冲区时,会从REPL_AUX链开始查找,如果REPL_AUX链上如果有可用的缓冲区,那么进程就能很快获取到缓冲区以便用于存储从磁盘读入的块。

那在REPL_AUX链上会不会有脏块呢?如果没有,那么进程在扫描REPL_AUX时会更快更简单。而答案是”在REPL_AUX链上是会存在脏块“的。下面用实验来验证一下,测试环境为Oracle 10.2.0.4 for Windows。
1. 准备测试数据:

create table test.t1 as select * from dba_objects;
insert into test.t1 select * from test.t1;
--多执行几次上面的insert.
commit;
--最终T1表的segment大小为72M左右。
create index test.t1_idx on test.t1(owner);

2. 将数据库buffer_cache设置为60M大小,重启数据库(注意sga_target参数值为0)。

3. 执行下面的查询:

select /*+ index(t1) */ sum(object_id) from test.t1 where owner='SYS' ;

4. 查询X$BH表里面挂在REPL_AUX链上的buffer:


SQL> select * from (
  2  select obj,dbarfil,dbablk,to_char(state,'xxxxxxxx') state,to_char(lru_flag,'xxxxxxxx') lru_flag,to_char(flag,'xxxxxxxx') flag
  3  ,tch,dirty_queue from x$bh where obj=24523 and state<>0 and bitand(lru_flag,4)=4 order by dbablk
  4  ) where rownum<=10;

       OBJ    DBARFIL     DBABLK STATE     LRU_FLAG  FLAG             TCH DIRTY_QUEUE
---------- ---------- ---------- --------- --------- --------- ---------- -----------
     24523          8      16743         1         6     80000          0           0
     24523          8      27850         1         6     80000          0           0
     24523          8      27938         1         6     80000          0           0
     24523          8      28895         1         6     80000          0           0
     24523          8      29620         1         6     80000          0           0
     24523          8      29692         1         6     80000          0           0
     24523          8      29830         1         6     80000          0           0
     24523          8      29842         1         6     80000          0           0
     24523          8      29906         1         6     80000          0           0
     24523          8      29980         1         6     80000          0           0

已选择10行。

LRU_FLAG是一个按位的标志,4表示”on auxiliary list(在辅助链表上)“,而上面的结果中LRU_FLAG为6,即2+4,说明这些buffer都在REPL_AUX链上。

5. 更新表T1中的一行数据:


SQL> select dbms_rowid.rowid_create(1,24523,8,16743,1) from dual;

DBMS_ROWID.ROWID_C
------------------
AAAF/LAAIAAAEFnAAB

更新这一行:

update test.t1 set object_name='b' where rowid='AAAF/LAAIAAAEFnAAB';
commit;

6. 再次检查X$BH中刚刚更新的那个块:

SQL> select obj,dbarfil,dbablk,to_char(state,'xxxxxxxx') state,to_char(lru_flag,'xxxxxxxx') lru_flag,to_char(flag,'xxxxxxxx') flag

  2  ,tch,dirty_queue from x$bh where obj=24523 and dbablk=16743 order by dbablk;

       OBJ    DBARFIL     DBABLK STATE     LRU_FLAG  FLAG             TCH DIRTY_QUEUE
---------- ---------- ---------- --------- --------- --------- ---------- -----------
     24523          8      16743         1         6   2002001          1           0

上面的结果中,FLAG也是按位表示的标示,最右边的1(即最低位的1)表示块是脏块(dirty buffer,从v$gbh的视图定义也能看到)。而LRU_FLAG=6表示buffer仍然还在REPL_AUX链上。

7. 在数据库上不做任何操作,过一段时间(大约5分钟之后),DBW进程会将刚才更改的脏块写到磁盘,再次检查X$BH:

SQL> select obj,dbarfil,dbablk,to_char(state,'xxxxxxxx') state,to_char(lru_flag,'xxxxxxxx') lru_flag,to_char(flag,'xxxxxxxx') flag

  2  ,tch,dirty_queue from x$bh where obj=24523 and dbablk=16743 order by dbablk;

       OBJ    DBARFIL     DBABLK STATE     LRU_FLAG  FLAG             TCH DIRTY_QUEUE
---------- ---------- ---------- --------- --------- --------- ---------- -----------
     24523          8      16743         1         6   2202000          1           0

在上面的结果中可以看到,LRU_FLAG仍然为6,表示仍然在REPL_AUX链上,而FLAG最低位从原来的1变成了0,表示已经不是脏块了。FLAG的从左向边第2个“2”数字(原来是0)表示"Buffer has been written once(缓冲区已经写过)"。

从上面的测试可以表明,在REPL_AUX链上是可能存在脏块的。不过REPL_AUX链上存在脏块的可能性非常小,其原因在于,REPL_AUX链上主要是很少被再次访问的块:一致性读的块不可能被修改;大表的全表扫描的块(“大表”的概念在《Oracle Core》这本书中有提到,主要涉及_small_table_threshold这个隐含参数),很少有对整个大表的所有块进行修改;WRITE_AUX链上的buffer在写完后会放回REPL_AUX链,不过这样的块被重新修改的可能性较小,因为WRITE_MAIN和WRITE_AUX的块来源于REPL链上较早之前修改过并且很少被访问的块,从概率上说被再次修改的可能性很小。所以REPL_AUX链上通常都是可以马上能够被重用的buffer。

通过类似的测试还可以说明两点:

  1. buffer在三个LRU子链(REPL_MAIN/REPL_AUX/WRITE_MAIN/)上移动,主要是由进程在寻找可用的buffer时由该进程移动。而buffer在另三个LRU子链(WRITE_MAIN/WRITE_AUX/REPL_AUX)上的移动由数据库写进程(DBW)来完成。这里要说明的是,DBW进程只有在是由前台进程触发的数据库写操作之后才会将buffer从WRITE_AUX移到REPL_AUX链上,而由检查点触发的写操作,不会使buffer在LRU的链上移动。
  2. 只有进程在读磁盘或通过clone产生一致性读需要buffer时,才会扫描LRU链并使buffer在LRU的四个子链上移动,而update这类DML操作在修改内存中的块时,是不会使buffer在LRU四个子链上移动的,所以如果REPL_AUX链上的buffer修改了,它也不会移动,仍然在REPL_AUX链上,使得REPL_AUX链上出现脏块。

--the end

,

《ORA-1555错误解决一例》一文中,当时尝试模拟UNDO段头事务表被覆盖的情况下出现ORA-01555错误,没有成功。实际上没有成功的原因是事务数虽然多,但是事务过小,使UNDO块没有被覆盖完,这样通过回滚事务表仍然能够得到事务表以前的数据。本文进一步讨论一些有关延迟块清除和一致性读方面的内容(但不会涉及到延迟块清除和一致性读的具体概念和过程,只是一些有趣的东西)。

先来看看一个数据块中ITL的转储:

Block header dump:  0x0200410a
 Object id on Block? Y
 seg/obj: 0x5f07  csc: 0xb08.1303a672  itc: 3  flg: -  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0001.025.0000472d  0x00000000.0000.00  C---    0  scn 0x0b08.12f461da
0x02   0x0007.015.00004ba0  0x0080d9a0.16f7.39  C-U-    0  scn 0x0b08.12fb5cae
0x03   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

我们看看ITL中的第2个条目,其Flag为"C-U-",C表示commit(提交),U表示Upper bound(上限),这里”U“表明ITL中记录的事务的提交SCN并不是精确的,只是表明事务提交的精确SCN是在ITL中记录的SCN之前,对于第2条ITL来说,其事务提交SCN小于或等于”0x0b08.12fb5cae“。那么这里的问题是:Upper bound是在什么情况下出现的?如果一个SQL语句对该块进行一致性读时,发现ITL中的Upper bound的SCN比一致性读需要的SCN大,这时会发生什么?要回答这些问题,先来看下面的一系列测试过程:

1. 在会话1中建测试表t1,将插入500行数据,每个块只有1行数据,一共500个块,然后再创建一个较大的测试表t2,插入1000行数据:

SQL> @mysid

       SID
----------
       160

SQL> create table t1 ( id number, small_vc varchar2(20),padding varchar2(1000)) pctfree 90 pctused 10;

表已创建。

SQL> insert /*+ append */ into t1
  2  select rownum,rownum || lpad('0',10,'0'),lpad('0',1000,'0')
  3  from dba_objects
  4  where rownum< =500;

已创建500行。

SQL>
SQL> commit;

提交完成。

SQL> create table t2 (id number,vc varchar2(20),padding varchar2(1000)) pctfree 90 pctused 10;

表已创建。

SQL> insert /*+ append */ into t2
  2  select rownum,lpad(rownum,20,'0'),lpad(rownum,1000,'0')
  3  from dba_objects
  4  where rownum<=1000;

已创建1000行。

SQL> commit;

提交完成。

SQL> select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid)
  2  from t1
  3  where rownum<=5;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------ ------------------------------------
                                   8                                16650
                                   8                                16651
                                   8                                16652
                                   8                                16653
                                   8                                16654

2. 在会话1中更新测试表T1中的所有行,并获取事务ID,然后再dump1个数据块和事务对应的UNDO段头块

SQL> update t1 set padding=lower(padding);

已更新500行。

SQL> select xidusn,xidslot,xidsqn,to_char(start_scnw,'xxxxxxxx') start_scnw,
  2  to_char(start_scnb,'xxxxxxxx') start_scnb,
  3  start_scnb+start_scnw*power(2,32) start_scn from v$transaction;

    XIDUSN    XIDSLOT     XIDSQN START_SCN START_SCN         START_SCN
---------- ---------- ---------- --------- --------- -----------------
         7         21      19360       b08  12f461db    12129305649627

SQL> select file_id,block_id from dba_rollback_segs where segment_name='_SYSSMU7$';

   FILE_ID   BLOCK_ID
---------- ----------
         2        105

SQL> alter system dump datafile 8 block 16650;

系统已更改。

SQL> alter system dump datafile 2 block 105;

系统已更改。

事务使用的事务表在回滚段_SYSSMU7$上,即第7个回滚段。事务表中的条目为21,即事务表中的第21条记录。

数据块dump出来的结果是(去掉了对本文话题无关紧要的内容,以后也是如此):


*** 2012-05-26 11:14:38.439
Start dump data blocks tsn: 8 file#: 8 minblk 16650 maxblk 16650
buffer tsn: 8 rdba: 0x0200410a (8/16650)
scn: 0x0b08.12f461f5 seq: 0x01 flg: 0x00 tail: 0x61f50601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1

Block header dump:  0x0200410a
 Object id on Block? Y
 seg/obj: 0x5f07  csc: 0xb08.12f461ce  itc: 3  flg: -  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0001.025.0000472d  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
0x02   0x0007.015.00004ba0  0x0080d9a0.16f7.39  ----    1  fsc 0x0000.00000000
0x03   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

可以看到ITL中的第2条正是当前活动事务在这个块上所使用的ITL。Xid为“0x0007.015.00004ba0”,转换成10进制正是“7.21.19360”,这跟之前查询出来的事务ID是一致的。ITL中此时的flag为"----",正是活动事务的标志。由于块中只有1行数据,因此Lck为1,即该事务在这个块中锁住的行数为1行。

下面再来看看此时UNDO段头块的转储结果:

Read the rest of this entry

,

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补丁集上。