Where is the backup of ASM disk header block?

在Oracle 10.2.0.5之前,ASM磁盘的头块并没有自己的备份,因此一旦头块损坏,如果没有以前kfed read备份出来的信息,也就没有办法使用kfed merge来作头块恢复,特别是如果一个磁盘组中所有的磁盘头块都出现问题(比如被人为地创建了PV),恢复ASM磁盘头块的操作就会非常麻烦。

但是从Oracle 10.2.0.5之后,ASM磁盘的头块会自动备份在另外一个块中,这实际上是Oracle 11g出现的功能,不过经过测试,在Oracle 10.2.0.5版本中,这个备份也是存在的。

正是因为存在这个备份,所以Oracle 10.2.0.5之后的kfed程序才有了新的repair命令,该命令将备份块直接覆盖到磁盘头块,完成修复工作。

在Oracle 10.2.0.4中,如果尝试执行kfed repair,则会报错说命令行参数不正确,此报错说明并不存在repair命令:

$ kfed repair 
KFED-00101: LRM error [102] while parsing command line arguments

但是在Oracle 10.2.0.5中,执行kfed repair,则会说无法打开文件空,而这正说明repair命令是存在的,报错是因为还需要明确指定要修复哪块磁盘:

$ kfed repair
KFED-00303: unable to open file ''

那么这个备份块具体存在哪里呢?在Solaris下的测试,我们使用truss来进行跟踪。

$ truss -o tracedisk2.out kfed repair /asmdisks/vdisk2

在trace文件中,找到下面这段,可以明确地看到kfed程序从第510个块中读出4096字节,然后再写回到第0个块中。

stat("/asmdisks/vdisk2", 0xFFFFFD7FFFDFDB20)    = 0
open("/asmdisks/vdisk2", O_RDWR)                = 7
lseek(7, 0x001FE000, SEEK_SET)                  = 2088960	<-- 1FE is 510
read(7, "01820101FE\0\0\0\0\0\080".., 4096)     = 4096		<-- read 4096 bytes
lseek(7, 0, SEEK_SET)                           = 0		
read(7, "01820101\0\0\0\0\0\0\080".., 4096)     = 4096		
lseek(7, 0, SEEK_SET)                           = 0		<-- 0 is 0
write(7, "01820101\0\0\0\0\0\0\080".., 4096)    = 4096		<-- write 4096 bytes
close(7)

同样如果是在Linux下用裸设备作为ASM磁盘,并且用strace进行repair命令的跟踪,也可以得到类似结果。

open("/dev/raw/raw3", O_RDWR)           = 5
lseek(5, 2088960, SEEK_SET)             = 2088960	<-- 2088960/4096=510
read(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
lseek(5, 0, SEEK_SET)                   = 0
read(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
lseek(5, 0, SEEK_SET)                   = 0
write(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
close(5)                                = 0

那么通过kfed命令再来验证一下这两个块是否都标志为头块。验证结果表示块类型都为DISKHEAD。

$ kfed read /asmdisks/vdisk2 blkn=0 | grep KFBTYP
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD
$ kfed read /asmdisks/vdisk2 blkn=510 | grep KFBTYP
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD <-- this is the backup!!

那么下一个疑问是,在11gR2以后,ASM磁盘组的AU Size可以指定不同的大小,是不是不同的AU Size下的磁盘头块备份都是在第510个块呢?还是用truss来跟踪一下,这里的vdisk3属于一个AU Size=8M的磁盘组,此时repair命令需要明确指定aus,否则会报KFED-00320错误。

truss -o tracedisk3.out kfed repair /asmdisks/vdisk3 aus=8388608

在trace文件中,可以发现已经不再是去读第510个块,而是改为读第4094个块。

stat("vdisk3", 0xFFFFFD7FFFDFDB10)              = 0
open("vdisk3", O_RDWR)                          = 7
lseek(7, 0x00FFE000, SEEK_SET)                  = 16769024	<--FFE is 4094
read(7, "01820101FE07\0\0\0\0\080".., 4096)     = 4096
lseek(7, 0, SEEK_SET)                           = 0
read(7, "01820101\0\0\0\0\0\0\080".., 4096)     = 4096
lseek(7, 0, SEEK_SET)                           = 0
write(7, "01820101\0\0\0\0\0\0\080".., 4096)    = 4096
close(7)

用kfed验证第4094个块,确实标志为DISKHEAD。

$ kfed read /asmdisks/vdisk3 blkn=4094 | grep KFBTYP
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD

那么也就是AU 1M的磁盘组头块备份在第510个块上,而AU 8M的磁盘组头块备份在第4094个块上,备份块的存储位置有规律吗?有的,始终保存在第2个AU的倒数第2个块上。下面来验证这个观点。

对于默认的磁盘组,AU Size=1M,每个AU中可以存储256个块,块号为0-255。第1个AU存储256个块,第2个AU最后1个块号为255,倒数第2个块号是254,也就是整体的第510个块(从第1个AU的第1个块往后算起)。

$ kfed read /asmdisks/vdisk2 blkn=0 | grep ausize
kfdhdb.ausize:                  1048576 ; 0x0bc: 0x00100000
$ kfed read /asmdisks/vdisk2 blkn=0 | grep blksize
kfdhdb.blksize:                    4096 ; 0x0ba: 0x1000
$ let r=1048576/4096;echo $r
256
$ let r=256+255-1;echo $r
510

对于AU Size=8M的磁盘组,每个AU可以存储2048个块,块号为0-2047。第1个AU存储2048个块,第2个AU最后1个块号为2047,倒数第2个块号是2046,也就是整体的第4094个块(从第1个AU的第1个块往后算起)。

$ kfed read /asmdisks/vdisk3 blkn=0 | grep ausize
kfdhdb.ausize:                  8388608 ; 0x0bc: 0x00800000
$ kfed read /asmdisks/vdisk3 blkn=0 | grep blksize
kfdhdb.blksize:                    4096 ; 0x0ba: 0x1000
$ let r=8388608/4096;echo $r
2048
$ let r=2048+2047-1;echo $r
4094

对于其它AU Size磁盘组的验证,看到文章的朋友有兴趣可以自己做一下。

结论:
从Oracle 10.2.0.5开始,ASM磁盘已经开始自动将头块进行备份,备份块的位置在第2个AU的倒数第2个块上(对于默认1M的AU来说,是第510个块),如果头块损坏,可以用kfed repair命令来修复。因此对于选用ASM存储作为生产环境的用户来说,尽快升级到10.2.0.5是明智的选择。

How to use asmca to create ASM instance silently in Oracle 11gR2

对于只能登录字符界面的很多服务器而言,能够使用silent模式创建一些组件是至关重要的。本文介绍如何在字符界面下使用asmca来创建ASM实例。

测试环境:Oracle 11.2.0.3 + Oracle Restart 【关于Oracle Restat,可以参看我之前的文章-How to Use Oracle Restart in Oracle 11gR2

在没有创建ASM实例之前,Clusterware中的资源状况如下:

grid@solaris:~$ ct
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS       
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.LISTENER_SOLARIS.lsnr
               ONLINE  ONLINE       solaris                                      
ora.ons
               OFFLINE OFFLINE      solaris                                      
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
      1        OFFLINE OFFLINE                                                   
ora.diskmon
      1        OFFLINE OFFLINE                                                   
ora.evmd
      1        ONLINE  ONLINE       solaris                                      
ora.orcl.db
      1        ONLINE  ONLINE       solaris                  Open                

通过如下命令用gird用户创建ASM实例。

asmca -silent -configureASM -sysAsmPassword oracle -asmsnmpPassword oracle -diskString '/asmdisks/vdisk*' -diskGroupName DATADG -disk '/asmdisks/vdisk1' -redundancy EXTERNAL

-silent:表示静默模式,这在字符界面中至关重要。
-configureASM:表示本命令是用于配置ASM实例,如果ASM实例不存在,则会新建。
-sysAsmPassword:sysasm权限用户的密码。
-asmsnmpPassword:asmsnmp权限用户的密码。如果用过asmca图形界面的应该记得这两个密码在图形界面中都是需要输入的。
-diskString:ASM磁盘组的磁盘发现路径。
-diskGroupName:需要新建的磁盘组名称。
-disk:ASM磁盘名称。
-redundancy:磁盘冗余方式,本例中使用了外部冗余。

会有大量的屏幕输出,但是最后显示如下行表示命令成功完成。

ASM created and started successfully.

Disk Group DATADG created successfully.

再次检查Clusterware的资源状况,可以看到已经增加了ASM资源,并且已经正常启动。

grid@solaris:~$ ct
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS       
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.LISTENER_SOLARIS.lsnr
               ONLINE  ONLINE       solaris                                      
ora.asm
               ONLINE  ONLINE       solaris                  Started             
ora.ons
               OFFLINE OFFLINE      solaris                                      
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
      1        ONLINE  ONLINE       solaris                                      
ora.diskmon
      1        OFFLINE OFFLINE                                                   
ora.evmd
      1        ONLINE  ONLINE       solaris                                      
ora.orcl.db
      1        ONLINE  ONLINE       solaris                  Open                

查看ASM磁盘组状况。

ASMCMD> lsdg
State    Type    Rebal  Sector  Block       AU  Total_MB  Free_MB  Req_mir_free_MB  Usable_file_MB  Offline_disks  Voting_files  Name
MOUNTED  EXTERN  N         512   4096  1048576       976      917                0             917              0             N  DATADG/

接下来增加一个磁盘到该磁盘组中,同样通过asmca来完成,当然这个操作也可以用SQL*Plus中的alter diskgroup命令来完成。

asmca -silent -addDisk -diskGroupName DATADG -disk '/asmdisks/vdisk2' -sysAsmPassword oracle

同样会有大量输出,最后显示如下行表示操作完成。

Disks added successfully to disk group DATADG

再次检查ASM磁盘组状况。可以看到磁盘组容量已经扩充。

ASMCMD> lsdg
State    Type    Rebal  Sector  Block       AU  Total_MB  Free_MB  Req_mir_free_MB  Usable_file_MB  Offline_disks  Voting_files  Name
MOUNTED  EXTERN  N         512   4096  1048576      1952     1891                0            1891              0             N  DATADG/  

如果想获得asmca的帮助,可以通过以下命令。

asmca -help

How to resolve ORA-19706 error when select from dblink

由于通过database link进行远程数据库的数据读取时,会自动同步当前库和远程库的SCN,老杨在文章中做过描述,并且提到:

而对于实际环境中,SCN的增加可能是几倍、几十倍甚至是上百倍,从而引发一些其他的bug。

下面就来看一个问题。该错误会在应用了2012年1月份CPU的Oracle数据库中出现,而在原始版本中,比如10.2.0.5.0之中不会出现此错误。

--通过dblink进行简单的查询报ORA-19706错误
SQL> select sysdate from dual@AIX12;
 
select sysdate from dual@AIX12
 
ORA-19706: invalid SCN

--登录远程库,检查SCN,很大的一个数字
SQL> select current_scn from vdatabase; 
CURRENT_SCN
-----------
12763142641

--而当前库,由于新创建,所以SCN并不大,与远程库想比相差几个数量级
SQL> select current_scn from vdatabase;
 
CURRENT_SCN
-----------
    5017684

看一下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是一个可以容纳很长时间的数字,为什么会出现too large的情况呢?这是由于SCN有headroom限制的原因,headroom是一个固定值,从1988年开始计算,以每秒16K的速度递增。当突然请求的SCN超过跟SCN headroom之间允许的差值时,则会出现ORA-19706错误。

详细的解释可以参看MOS Note – System Change Number (SCN), Headroom, Security and Patch Information [ID 1376995.1]。

解决方法:
1. 设置隐含参数_external_scn_rejection_threshold_hours,具体解释可以参看:
MOS Note – Installing, Executing and Interpreting output from the “scnhealthcheck.sql” script [ID 1393363.1]。

该参数在10.2.0.5中默认是744,设置为24通常可以解决问题(表示允许跟headroom之间的差距相差24小时)。

SQL> alter system set "_external_scn_rejection_threshold_hours"=24 scope=spfile;

也就是如果远端数据库的SCN由于某些bug导致异常增长,那么这个SCN跟headroom之间的差距将会低于默认值744小时(31天),当本地数据库(打了2012年1月份CPU补丁后的)通过dblink查询远端数据,由于SCN同步机制,本地数据库尝试将SCN同步为跟远端一样大小,但是这个值超过了跟SCN headroom之间允许的差值,因此报错。

实际上具体设置的值应该根据上面MOS文档中scnhealthcheck.sql的结果,该SQL应该在远端数据库中运行(或者说在具有最大的SCN的数据库中运行),假设运行结果显示:

SCN Headroom: 14.55

那么则表示目前SCN距离headroom只有14.55天的空间,此时我们将_external_scn_rejection_threshold_hours参数设置为13*24=312,即可解决问题。但是要注意,远端数据库SCN仍在不停异常增长,等到某一天远端数据库的SCN Headroom降低为小于13天的时候,ORA-19706错误又会再次出现。

2. 回滚打上的2012年1月份CPU。当然这并不是推荐的方法。

3. 最终极的解决方案,将环境中所有通过dblink互相连接的数据库全部打上最新的PSU,比如目前10205的最新PSU是10.2.0.5.7,这将会解决所有SCN异常增长的问题,只要数据库每秒增长的SCN不会超过16K,那么就会离headroom越来越远。