1.前言
ASM(Automatic Storage Management)是Oracle主推的一种面向Oracle的存储解决方案,它是一个管理卷组或者文件系统的软件,目前已经被RAC环境广泛使用,但是ASM由于其高度的封装性,使得我们很难知道窥探其内部的原理。ASM如果一旦出现问题,通常都很难处理。即便在有很完备的RMAN备份的情况下,恢复起来都可能需要很长的时间。
简单的讲ASM是通过一个ASM实例来实现对磁盘的管理。这个和Oracle 实例很类似。ASM实例也有SGA和后台进程组成。但是ASM实例相对的工作很少,所以它的SGA 相对较小,其主要维护一些ASM的元数据,包括磁盘组元数据(disk group metadata)、磁盘元数据(disk metadata)、文件元数据(file metadata),ASM的体系结构如下图:
ASM实例管理ASM磁盘组由一个个的ASM磁盘组成,每个磁盘又由多个区组成,每个区又由多个AU组成,它们共同协作就构成了ASM的基础应用。
2.ASM体系结构
前面说了ASM中的文件总体上来说分为两大类,元文件和数据文件。数据文件包含Oracle的数据文件、控制文件、重做日志文件、归档日志文件等等。对于ASM来说,只要是非元文件,就是数据文件。
每一个文件在ASM中都有一个专门的索引号,也就是编号,ASM文件索引号从1开始。其中前255个,也就是1至255号文件,都是元文件。256之后的是其他各种文件。
元文件中包含了各种ASM的配置、各类数据文件信息还有目录、别名等等信息,都是在元文件中的。所有V$ASM_开头视图的信息,都来自元文件中。
其中,1号文件包含所有文件的磁盘占用信息,包括元文件、甚至1号文件自身的空间分布信息,也都是在1号文件内部。每个文件在它里面占用一个块(4096字节,元数据块大小为4K)的空间。
从256号文件开始,是数据库的各类文件。假设你放在ASM上的第一个文件是一个控制文件A,第二个文件是一个数据文件B。哪么控制文件A在ASM中的索引号是256,数据文件B的索引号是257,但是这255个元文件并不是都有内容,其中只有前9个文件有内容,我们可以通过访问x$kffxp内部表获取相关的信息,如下:
SQL>SELECT number_kffxp file#, disk_kffxp disk#, COUNT(disk_kffxp) extents
2 FROMx$kffxp
3 WHERE group_kffxp=1
4 AND disk_kffxp <> 65534
5 GROUP BY number_kffxp, disk_kffxp
6 ORDER BY 1;
FILE# DISK# EXTENTS
-------------------- ----------
1 0 2
2 0 1
3 0 28
3 3 14
4 0 5
4 3 3
5 0 1
6 0 1
8 3 1
9 0 1
256 0 482
.........
268 3 7
269 0 1
35 ROWSselected.
file# 1 -file directory
file# 2 -disk directory
file# 3 -active Change Directory(ACD)
file# 4 -continuing Operations Directory (COD)
file# 5 -template directory
file# 6 -alias directory
file# 7 -volume directory
file# 8 -diskUsed Space Directory (USD)
file# 9 -attributes directory
其中1号文件总是开始在0号磁盘2号AU,这是ASM中定位文件的起点,它的作用,有点相当于磁盘上的引导区,在电脑开机后负责将OS启动起来。每次从ASM中读数据时,Oracle都要先读到1号文件,从中找出要读的目标文件在磁盘上的分布位置,然后再去读取相应的文件的数据。
那么问题来了,0号AU存放的什么信息呢?这个我想大家已经猜到了,就是存放的ASM磁盘头(disk header)信息,也是我们随后会重点介绍的内容,一旦该磁盘配置好后,如果不再对其做其它的操作,这部分内容相对就固定不变,但是ASM中最为脆弱的又是ASM磁盘头。如果磁盘头逻辑损坏了,即corrupt了,整个磁盘组将不能够mount,依赖于ASM实例的数据库也将不能够访问ASM磁盘并正常启动。后续我们会通过kfed工具对其进行解读。
3.kfod和kfed
kfod和kfed是ASM自带的未公开的工具,kfod主要用于在系统级别对ASM信息的查看,而kfed它可以读取和修改ASM磁盘的元数据,对修复一些关键错误非常有用,接下来分别介绍和演示下这两个工具。(以下使用的环境数据库版本都是11gr2)
3.1. kfod介绍
我们以前要想访问和查询ASM的信息,只能通过数据库视图进行查询,无法通过操作系统层进行访问,但是现在我们可以直接通过kfod工具,直接在操作系统层对ASM的相关信息进行查阅,我们先来看下kfod的帮助,输入kfod –h得到结果如下:
[oracle@rac01~]$ kfod -h
KFOD-00101:LRM error [107] while parsing command line arguments
_asm_a/llow_only_raw_disks KFOD allow only raw devices[_asm_allow_only_raw_disks=TRUE/(FALSE)]
_asm_l/ibraries ASMLibraries[_asm_libraries=lib1,lib2,...]
_asms/id ASM Instance[_asmsid=sid]
a/sm_diskstring ASM Diskstring[asm_diskstring=discoverystring, discoverystring ...]
c/luster KFOD cluster[cluster=TRUE/(FALSE)]
db/_unique_name db_unique_name for ASMinstance[db_unique_name=dbname]
di/sks Disks to discover[disks=raw,asm,badsize,all]
ds/cvgroup Include group name[dscvgroup=TRUE/(FALSE)]
g/roup Disks in diskgroup[group=diskgroup]
h/ostlist hostlist[hostlist=host1,host2,...]
metadata_a/usize AU Size for Metadata SizeCalculation
metadata_c/lients Client Count for Metadata SizeCalculation
metadata_d/isks Disk Count for Metadata SizeCalculation
metadata_n/odes Node Count for Metadata SizeCalculation
metadata_r/edundancy Redundancy for Metadata SizeCalculation
n/ohdr KFOD header suppression[nohdr=TRUE/(FALSE)]
o/p KFOD options type[OP=DISKS/CANDIDATES/MISSING/GROUPS/INSTS/VERSION/CLIENTS/RM/RMVERS/DFLTDSTR/GPNPDSTR/METADATA/ALL]
p/file ASM parameter file[pfile=parameterfile]
s/tatus Include disk header status[status=TRUE/(FALSE)]
v/erbose KFOD verbose errors[verbose=TRUE/(FALSE)]
我们可以看到,有大量的参数,几乎可以轻松的查看ASM的所有信息,如我们以前要查询ASM的磁盘空间大小和剩余量,必须通过登录实例访问视图,现在就可以直接通过kfod工具查询,如下:
l 查看磁盘组信息
[oracle@rac01~]$ kfod di=all op=groups
--------------------------------------------------------------------------------
Group Size Free Redundancy Name
================================================================================
1: 16378 Mb 14070 Mb EXTERN DATA
2: 509 Mb 459 Mb EXTERN ARCH
l 查看磁盘组的组成
[oracle@rac01~]$ kfod di=all group=diskgroup ds=true
--------------------------------------------------------------------------------
Disk Size Path DiskGroup User Group
================================================================================
1: 8189 Mb /dev/asm-disk1 DATA oracle dba
2: 8189 Mb /dev/asm-disk2 DATA oracle dba
3: 509 Mb /dev/asm-disk3 ARCH oracle dba
3.2. kfed介绍
kfod只是可以通过操作系统直接查看ASM的信息,但是前面我们说了,ASM最易出现问题的不是存储数据的本身,而是ASM磁盘头,那如何访问和维护磁盘头呢?这就要用到我们介绍的kfed工具。KFED是ASM自带的一个未公开的工具,它可以对ASM元数据进行一系列的操作,重要的是它能够在ASM无法启动的时候也可以工作,对修复一些关键错误非常有用(前面介绍的kfod必须在ASM启动的状态下访问),我们同样先来看下帮助信息,如下:
[oracle@rac01~]$ kfed -h
as/mlib ASM Library [asmlib='lib']
aun/um AU number to examine or update[AUNUM=number]
aus/z Allocation Unit size in bytes[AUSZ=number]
blkn/um Block number to examine or update[BLKNUM=number]
blks/z Metadata block size in bytes[BLKSZ=number]
ch/ksum Update checksum before each write[CHKSUM=YES/NO]
cn/t Count of AUs to process[CNT=number]
de/v ASM device to examine or update[DEV=string]
dm/pall Don't suppress repeated lines whendumping corrupt blocks [DMPALL=YES/NO]
o/p KFED operation type[OP=READ/WRITE/MERGE/REPAIR/NEW/FORM/FIND/STRUCT]
p/rovnm Name for provisioning purposes[PROVNM=string]
s/eek AU number to seek to [SEEK=number]
te/xt File name for translated block text[TEXT=string]
ty/pe ASM metadata block type number[TYPE=number]
kfed可以跳过ASM实例直接访问磁盘,这就可以在一些非常规的环境下,如ASM关闭,磁盘组无法mount等对ASM的相关信息进行访问,在加上其具备的修改功能,可以利用其对ASM进行修复操作,要想了解kfed的修复方法,我们首先来了解下ASM磁盘头,前面我们通过kfod知道了有/dev/asm-disk1、/dev/asm-disk2、/dev/asm-disk3个磁盘作为ASM的磁盘组所使用,我们又知道,磁盘头的信息存放在每个磁盘0号AU的位置第0号数据块中,那么现在我们就以其中的一个来看一下它的磁盘头到底存储了那些信息,如下:
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=0 blkn=0
【01】kfbh.endian: 1 ; 0x000: 0x01
【02】kfbh.hard: 130 ; 0x001: 0x82
【03】kfbh.type: 1 ; 0x002:KFBTYP_DISKHEAD
【04】kfbh.datfmt: 1 ; 0x003: 0x01
【05】kfbh.block.blk: 0 ; 0x004: blk=0
【06】kfbh.block.obj: 2147483648 ; 0x008: disk=0
【07】kfbh.check: 875080645 ; 0x00c:0x3428abc5
【08】kfbh.fcn.base: 4892 ; 0x010: 0x0000131c
【09】kfbh.fcn.wrap: 0 ; 0x014: 0x00000000
【10】kfbh.spare1: 0 ; 0x018: 0x00000000
【11】kfbh.spare2: 0 ; 0x01c: 0x00000000
【12】kfdhdb.driver.provstr: ORCLDISK ; 0x000: length=8
【13】kfdhdb.driver.reserved[0]: 0 ; 0x008: 0x00000000
【14】kfdhdb.driver.reserved[1]: 0 ; 0x00c: 0x00000000
【15】kfdhdb.driver.reserved[2]: 0 ; 0x010: 0x00000000
【16】kfdhdb.driver.reserved[3]: 0 ; 0x014: 0x00000000
【17】kfdhdb.driver.reserved[4]: 0 ; 0x018: 0x00000000
【18】kfdhdb.driver.reserved[5]: 0 ; 0x01c: 0x00000000
【19】kfdhdb.compat: 186646528 ; 0x020: 0x0b200000
【20】kfdhdb.dsknum: 0 ; 0x024: 0x0000
【21】kfdhdb.grptyp: 1 ; 0x026:KFDGTP_EXTERNAL
【22】kfdhdb.hdrsts: 3 ; 0x027:KFDHDR_MEMBER
【23】kfdhdb.dskname: DATA_0000 ; 0x028: length=9
【24】kfdhdb.grpname: DATA ; 0x048: length=4
【25】kfdhdb.fgname: DATA_0000 ; 0x068: length=9
【26】kfdhdb.capname: ; 0x088: length=0
【27】kfdhdb.crestmp.hi: 33020845 ; 0x0a8: HOUR=0xdDAYS=0x1d MNTH=0x6 YEAR=0x7df
【28】kfdhdb.crestmp.lo: 1437992960 ; 0x0ac: USEC=0x0MSEC=0x182 SECS=0x1b MINS=0x15
【29】kfdhdb.mntstmp.hi: 33021392 ; 0x0b0: HOUR=0x10DAYS=0xe MNTH=0x7 YEAR=0x7df
【30】kfdhdb.mntstmp.lo: 2467747840 ; 0x0b4: USEC=0x0MSEC=0x1b6 SECS=0x31 MINS=0x24
【31】kfdhdb.secsize: 512 ; 0x0b8: 0x0200
【32】kfdhdb.blksize: 4096 ; 0x0ba: 0x1000
【33】kfdhdb.ausize: 1048576 ; 0x0bc: 0x00100000
【34】kfdhdb.mfact: 113792 ; 0x0c0: 0x0001bc80
【35】kfdhdb.dsksize: 8189 ; 0x0c4: 0x00001ffd
【36】kfdhdb.pmcnt: 2 ; 0x0c8: 0x00000002
【37】kfdhdb.fstlocn: 1 ; 0x0cc: 0x00000001
【38】kfdhdb.altlocn: 2 ; 0x0d0: 0x00000002
【39】kfdhdb.f1b1locn: 2 ; 0x0d4: 0x00000002
【40】kfdhdb.redomirrors[0]: 0 ; 0x0d8: 0x0000
【41】kfdhdb.redomirrors[1]: 65535 ; 0x0da: 0xffff
【42】kfdhdb.redomirrors[2]: 65535 ; 0x0dc: 0xffff
【43】kfdhdb.redomirrors[3]: 65535 ; 0x0de: 0xffff
【44】kfdhdb.dbcompat: 168820736 ; 0x0e0: 0x0a100000
【45】kfdhdb.grpstmp.hi: 33020845 ; 0x0e4: HOUR=0xdDAYS=0x1d MNTH=0x6 YEAR=0x7df
【46】kfdhdb.grpstmp.lo: 1437544448 ; 0x0e8: USEC=0x0MSEC=0x3cc SECS=0x1a MINS=0x15
【47】kfdhdb.vfstart: 352 ; 0x0ec: 0x00000160
【48】kfdhdb.vfend: 384 ; 0x0f0: 0x00000180
【49】kfdhdb.spfile: 0 ; 0x0f4: 0x00000000
【50】kfdhdb.spfflg: 0 ; 0x0f8: 0x00000000
【51】kfdhdb.ub4spare[0]: 0 ; 0x0fc: 0x00000000
。。。。。。(略)
【103】kfdhdb.ub4spare[52]: 0 ; 0x1cc: 0x00000000
【104】kfdhdb.ub4spare[53]: 0 ; 0x1d0: 0x00000000
【105】kfdhdb.acdb.aba.seq: 0 ; 0x1d4: 0x00000000
【106】kfdhdb.acdb.aba.blk: 0 ; 0x1d8: 0x00000000
【107】kfdhdb.acdb.ents: 0 ; 0x1dc: 0x0000
【108】kfdhdb.acdb.ub2spare: 0 ; 0x1de: 0x0000
总共有108行内容,这些内容是什么含义呢?我们来一一解读
第1行:kfbh.endian:小字节(LittleEndian) = 1 大字节(Big Endian) = 0
第2行:kfbh.hard:元文件块大小 默认是0x82
第3行:type_kfbh:类型,磁盘头总是KFBTYP_DISKHEAD
第4行:kfbh.datfmt 数据格式
第5行:kfbh.block.blk磁盘头始终是0
第6行:kfbh.block.obj磁盘在磁盘组中的号
第7行:kfbh.check 校验码,写到磁盘前计算
第8~11行:磁盘头无意义
第12行:kfdhdb.driver.provstr 没有使用Lib包的时候显示为ORCLDISK
第13~18行:kfdhdb.driver.reserved始终为0
第19行:kfdhdb.compat 版本号
第20行: kfdhdb.dsknum 0
第21行:kfdhdb.grptyp 冗余策略,共4种冗余策略
KFDGTP_INVALID ((kfdgtp)0) /* Illegal value */
KFDGTP_EXTERNAL ((kfdgtp)1) /* Externalredundancy */
KFDGTP_NORMAL ((kfdgtp)2) /*Normal redundancy */
KFDGTP_HIGH ((kfdgtp)3) /* High redundancy */
第22行:hdrsts_kfdhdb 磁盘状态,共8种状态
KFDHDR_INVALID ((kfdhdr)0) /* Illegal value */
KFDHDR_UNKNOWN ((kfdhdr)1) /* Disk header block unreadable */
KFDHDR_CANDIDATE ((kfdhdr)2) /* No OSM or OS disk header found */
KFDHDR_MEMBER ((kfdhdr)3) /* Normal member of the group */
KFDHDR_FORMER ((kfdhdr)4) /* Disk dropped cleanly from group */
KFDHDR_CONFLICT ((kfdhdr)5) /* Header conflicts */
KFDHDR_INCOMPAT ((kfdhdr)6) /* Written by incompatible software*/
KFDHDR_PROVISIONED ((kfdhdr)7) /* Disk wasprepared beforehand */
参见V$asm_disk
第23行:dskname_kfdhdb 磁盘名称
第24行:grpname_kfhdb 磁盘组名称
第25行:fgname_kfdhdb: 磁盘名称
第26行:capname_kfdhdb 未使用
第27~28行:fdhdb.crestmp.hi,kfdhdb.crestmp.lo创建时的时间戳
第29~30行:kfdhdb.mntstmp.hi,kfdhdb.mntstmp.lo Mount时的时间戳
第31行:kfdhdb.secsize 扇区大小
第32行:kfdhdb.blksize块大小
第33行:kfdhdb.ausize AU大小。
第34行:kfdhdb.mfact: 版本相关,无意义
第35行:kfhdb.dsksize本磁盘包含的AU数量。kfdhdb.ausize *dsksize_kfdhdb = disk size
第36行:kfdhdb.pmcnt AU的物理地址空间。
第37行:kfdhdb.fstlocn 说明第一个AU后,是用户可用空间。
第37行:kfdhdb.altlocn 说明文件目录开始自第二个AU
第38行:kfdhdb.f1b1locn 文件目录块1分配单元号
第39~108行:在默认au的情况下,后面的信息基本都变化不大
这部分信息对于ASM识别磁盘非常重要,也正式因为它的重要,11g默认就就对其进行了一个镜像备份,可以通过kefd的repair操作直接恢复,相当的方便。
4.ASM磁盘头的修复
由于磁盘头的信息相对简单而且变化很小,特别是同一磁盘组的磁盘,每个磁盘头的信息几乎一致,而当我们了解了磁盘头的信息后,一旦某个磁盘头的信息出现了问题,我们可以利用其他磁盘头信息然后通过kfed工具对其进行修改和调整,达到修复的目的。其中用得最多的是读取(Read)、修复(Repair)和合并(Merge)三个操作。
4.1. Repair修复法
首先大家要明确一点,Oracle正因为知道ASM磁盘头的重要性,因此默认情况下,就对其进行了镜像备份,有点类似于控制文件。在每个ASM文件的第一个AU位置,是磁盘头信息,而第1个AU位置最后第2个数据块中,默认情况下就是对磁盘头信息的镜像备份,如AU的缺省大小是1M时,那个数据块就是254,我们可以查询下:
磁盘头信息:
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=0 blkn=0|more
kfbh.endian: 1 ; 0x000: 0x01
kfbh.hard: 130 ; 0x001: 0x82
kfbh.type: 1 ; 0x002:KFBTYP_DISKHEAD
kfbh.datfmt: 1 ; 0x003: 0x01
kfbh.block.blk: 0 ; 0x004: blk=0
kfbh.block.obj: 2147483648 ; 0x008: disk=0
kfbh.check: 1997052059 ; 0x00c:0x7708989b
kfbh.fcn.base: 4892 ; 0x010: 0x0000131c
kfbh.fcn.wrap: 0 ; 0x014: 0x00000000
kfbh.spare1: 0 ; 0x018: 0x00000000
kfbh.spare2: 0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr: ORCLDISK ; 0x000: length=8
kfdhdb.driver.reserved[0]: 0 ; 0x008: 0x00000000
。。。。。。(以下内容略)
1号AU倒数第二个数据块信息:
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=1 blkn=254|more
kfbh.endian: 1 ; 0x000: 0x01
kfbh.hard: 130 ; 0x001: 0x82
kfbh.type: 1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt: 1 ; 0x003: 0x01
kfbh.block.blk: 254 ; 0x004: blk=254
kfbh.block.obj: 2147483648 ; 0x008: disk=0
kfbh.check: 1997052005 ; 0x00c:0x77089865
kfbh.fcn.base: 4892 ; 0x010: 0x0000131c
kfbh.fcn.wrap: 0 ; 0x014: 0x00000000
kfbh.spare1: 0 ; 0x018: 0x00000000
kfbh.spare2: 0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr: ORCLDISK ; 0x000: length=8
kfdhdb.driver.reserved[0]: 0 ; 0x008: 0x00000000
。。。。。。(以下内容略)
可以看到查询出来的内容同样也是磁盘头内容,这部分是ORACLE自行备份的信息,但是注意该特性只在10.2.0.5及以上版本才有,10.2.0.4版本数据库没有这部分信息。
利用dd命令清空其中一个ASM磁盘的磁盘头信息,如下
[oracle@rac01~]$ dd if=/dev/zero of=/dev/asm-disk1 bs=4096 count=1
1+0records in
1+0records out
4096bytes (4.1 kB) copied, 0.00157905 s, 2.6 MB/s
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=0blkn=0|more
kfbh.endian: 0 ; 0x000: 0x00
kfbh.hard: 0 ; 0x001: 0x00
kfbh.type: 0 ; 0x002: KFBTYP_INVALID
kfbh.datfmt: 0 ; 0x003: 0x00
kfbh.block.blk: 0 ; 0x004: blk=0
kfbh.block.obj: 0 ; 0x008: file=0
kfbh.check: 0 ; 0x00c:0x00000000
kfbh.fcn.base: 0 ; 0x010: 0x00000000
kfbh.fcn.wrap: 0 ; 0x014: 0x00000000
kfbh.spare1: 0 ; 0x018: 0x00000000
kfbh.spare2: 0 ; 0x01c: 0x00000000
7F8A21E3440000000000 00000000 00000000 00000000 [................]
Repeat 255 times
KFED-00322: Invalid contentencountered during block traversal: [kfbtTraverseBlock][Invalid OSM blocktype][][0]
可以看到,/dev/asm-disk1磁盘的磁盘头信息已经被彻底的损坏,我们再查看ASM视图,看下状态;
SQL>select group_Number,DISK_NUMBER,name,HEADER_STATUS,path from v$asm_disk;
GROUP_NUMBERDISK_NUMBER NAME HEADER_STATUS PATH
2 1 DATA_0001 MEMBER /dev/asm-disk2
2 0 DATA_0000 CANDIDATE /dev/asm-disk1
1 0 ARCH_0000 MEMBER /dev/asm-disk3
如果这时候重启服务器,CRS集群也将无法启动,CRS的一些关键信息也是存放在ASM中,以前遇到这种情况,我们只能重建ASM,利用备份进行恢复,但是现在我们晓得了磁盘头的备份机制,我们只需要执行一个简单的repair命令,就可以轻松的修复磁盘头信息,如下
利用repair命令恢复1号au中的备份信息到0号au磁盘头信息位置,如下
[oracle@rac01~]$ kfed repair /dev/asm-disk1
磁盘头的信息被备份信息恢复了,如下:
[oracle@rac01~]$ kfed read /dev/asm-disk1
kfbh.endian: 1 ; 0x000: 0x01
kfbh.hard: 130 ; 0x001: 0x82
kfbh.type: 1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt: 1 ; 0x003: 0x01
kfbh.block.blk: 0 ; 0x004: blk=0
kfbh.block.obj: 2147483648 ; 0x008: disk=0
kfbh.check: 1997052059 ; 0x00c:0x7708989b
kfbh.fcn.base: 4892 ; 0x010: 0x0000131c
kfbh.fcn.wrap: 0 ; 0x014: 0x00000000
kfbh.spare1: 0 ; 0x018: 0x00000000
kfbh.spare2: 0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr: ORCLDISK ; 0x000: length=8
kfdhdb.driver.reserved[0]: 0 ; 0x008: 0x00000000
。。。。。。。(以下内容略)
4.2. Merge修复法
Repair修复asm磁盘头信息是最简单、最方便的方法,它主要是利用了ORACLE自动镜像备份的原因,但是这个功能只在10.2.0.5之后才有,而且如果一旦备份地方(1号AU,倒数第二个块)的信息也被损坏,这个时候就无法利用Repair方式进行恢复了,如果没有备份,那里恢复起来就会比较的麻烦。但是如果是一个磁盘组的某个磁盘头出现了问题,我们可以结合保存在磁盘中的文件字典(file directory)和(diskdirectory)信息的来进行磁盘头恢复,由于文件字典(filedirectory),只保存在磁盘组中其中一个磁盘中,这里以/dev/asm-disk1和/dev/asm-disk2为例,因为是同属一个磁盘组,磁盘组名为DATA,如下:
[oracle@rac01~]$ kfod di=all group=diskgroup ds=true
--------------------------------------------------------------------------------
Disk Size Path DiskGroup User Group
================================================================================
1: 8189 Mb/dev/asm-disk1 DATA oracle dba
2: 8189 Mb/dev/asm-disk2 DATA oracle dba
3: 509 Mb /dev/asm-disk3 ARCH oracle dba
我们首先来看下,它们两个磁盘,谁是file directory磁盘,通过如下查询:
[oracle@rac01~]$ kfed read /dev/asm-disk1 |grep f1b1
kfdhdb.f1b1locn: 2 ; 0x0d4: 0x00000002
[oracle@rac01~]$ kfed read /dev/asm-disk2 |grep f1b1
kfdhdb.f1b1locn: 0 ; 0x0d4: 0x00000000
如果查询出来的结果为非0的,就是file directory磁盘,这里我们可以看到,是/dev/asm-disk1这个磁盘中保存的filecdirectory信息。
1. 确认disk directory在磁盘中的位置
[oracle@rac01~]$ kfed read /dev/asm-disk1 |grep f1b1明确file directory的位置,在2号au
kfdhdb.f1b1locn: 2; 0x0d4: 0x00000002
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=2 blkn=2 |grep au |head -5明确disk directory的位置
kfffde[0].xptr.au: 3 ; 0x4a0: 0x00000003
kfffde[1].xptr.au: 4294967295 ; 0x4a8: 0xffffffff
kfffde[2].xptr.au: 4294967295 ; 0x4b0: 0xffffffff
kfffde[3].xptr.au: 4294967295 ; 0x4b8: 0xffffffff
kfffde[4].xptr.au: 4294967295 ; 0x4c0: 0xffffffff
2. 查看磁盘字典信息,因为我们损坏的是/dev/asm-disk2,因此我们这里只关注kfddde[1]的信息,重点关注红色的6条信息。
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=3 blkn=0 |grep name
kfddde[0].dskname: DATA_0000 ; 0x038: length=9
kfddde[0].fgname: DATA_0000 ; 0x058: length=9
kfddde[1].dskname: DATA_0001 ; 0x1f8: length=9
kfddde[1].fgname: DATA_0001 ; 0x218: length=9
kfddde[2].dskname: ; 0x3b8: length=0
kfddde[2].fgname: ; 0x3d8: length=0
。。。。。。(以下内容省略)
这里我们知道DATA磁盘组包含了2个磁盘,其中DATA_0000是本磁盘,那么DATA_0001就是损坏的/dev/asm-disk2磁盘了,因此我们只需要查看涉及该磁盘的相关信息,如下:
[oracle@rac01~]$ kfed read /dev/asm-disk1 aun=3 blkn=0 |more
。。。。。。(略)
kfddde[1].entry.incarn: 1 ; 0x1e4: A=1 NUMM=0x0
kfddde[1].entry.hash: 1 ; 0x1e8: 0x00000001
kfddde[1].entry.refer.number:4294967295; 0x1ec: 0xffffffff
kfddde[1].entry.refer.incarn: 0 ; 0x1f0: A=0 NUMM=0x0
kfddde[1].dsknum: 1 ; 0x1f4: 0x0001
kfddde[1].state: 2 ; 0x1f6: KFDSTA_NORMAL
kfddde[1].ddchgfl: 132 ; 0x1f7: 0x84
kfddde[1].dskname: DATA_0001 ; 0x1f8: length=9
kfddde[1].fgname: DATA_0001 ; 0x218: length=9
kfddde[1].crestmp.hi: 33020874 ; 0x238: HOUR=0xa DAYS=0x1eMNTH=0x6 YEAR=0x7df
kfddde[1].crestmp.lo: 574201856 ; 0x23c: USEC=0x0 MSEC=0x268SECS=0x23 MINS=0x8
kfddde[1].failstmp.hi: 0 ; 0x240: HOUR=0x0 DAYS=0x0MNTH=0x0 YEAR=0x0
kfddde[1].failstmp.lo: 0 ; 0x244: USEC=0x0 MSEC=0x0SECS=0x0 MINS=0x0
kfddde[1].timer: 0 ; 0x248: 0x00000000
kfddde[1].size: 8189 ; 0x24c: 0x00001ffd
。。。。。。(略)
3. 根据以上信息,生成/dev/asm-disk2的磁盘头信息(这里可以通过/dev/asm-disk1的磁盘头信息修改)
[oracle@rac01 ~]$ kfed read /dev/asm-disk1aun=0 blkn=0 text=/home/oracle/asm-disk1.txt
利用编辑器打开生成的文件asm-disk1.txt,主要对应修改以下内容
原始的
kfbh.block.obj: 2147483648 ; 0x008: disk=0
kfdhdb.dsknum: 0 ; 0x024: 0x0000
kfdhdb.dskname: DATA_0000 ; 0x028: length=9
kfdhdb.fgname: DATA_0000 ; 0x068: length=9
kfdhdb.crestmp.hi:33020845 ; 0x0a8: HOUR=0xd DAYS=0x1dMNTH=0x6 YEAR=0x7df
kfdhdb.crestmp.lo:1437992960 ; 0x0ac: USEC=0x0 MSEC=0x182 SECS=0x1b MINS=0x15
kfdhdb.dsksize: 8189; 0x0c4: 0x00001ffd
kfdhdb.f1b1locn: 2 ; 0x0d4: 0x00000002
修改后的
kfbh.block.obj: 2147483649 ; 0x008: disk=1 (按顺序加1)
kfdhdb.dsknum: 1 ; 0x024: 0x0001 (参照kfddde[1].dsknum)
kfdhdb.dskname: DATA_0001 ; 0x028: length=9 (参照kfddde[1].dskname)
kfdhdb.fgname: DATA_0001 ; 0x068: length=9 (参照kfddde[1].fgname)
kfdhdb.crestmp.hi:33020874; 0x0a8: HOUR=0xd DAYS=0x1d MNTH=0x6 YEAR=0x7df (参照kfddde[1].crestmp.hi)
kfdhdb.crestmp.lo:574201856; 0x0ac: USEC=0x0 MSEC=0x182SECS=0x1b MINS=0x15 (参照kfddde[1].crestmp.lo)
kfdhdb.dsksize: 8189; 0x0c4: 0x00001ffd (参照kfddde[1].size)
kfdhdb.f1b1locn: 0 ; 0x0d4: 0x00000000 (因为asm-disk2为非file directory文件)
修改完成后,保存为新的文本名称为asm-disk2.txt,最后利用merge命令,写入/dev/asm-disk2磁盘头中,如下:
[oracle@rac01~]$ kfed merge /dev/asm-disk2 text=/home/oracle/asm-disk2.txt
来总结下利用merge修复的基本步骤,1.首先确认file directory的位置,再根据filedirectory找到dis directory的位置;2.根据disk directory找出磁盘信息,手工编辑磁盘头文件;3最后用kfed merge到对应的磁盘头中,生成磁盘头信息。
最后提一下,一般情况,file directory在磁盘组某个磁盘的au=2的位置,但是如果对磁盘组进行了增加和删除操作,就可能需要手工去查询了。
5.amdu工具使用
前面我们介绍了对磁盘头信息的修复,接下来我们介绍一个对存放在ASM里面的数据进行抽取的工具amdu。我们知道,如果asm实例是启动状态,我们可以通过asmcd或者rman命令将asm中的文件提取出来,但是如果asm无法启动的情况下,以上2个工具就无发实现了,这时候我们就可以用amdu来进行数据文件的抽取,这对于极端情况下的数据恢复是非常有帮助的。
AMDU是Oracle 11g里自带的一个免费的工具,用于分析ASM磁盘组的元数据以及从不能mount的磁盘组中往外抽取数据文件,它的原理就是跳过磁盘头直接解析磁盘中的filedirectory部分的内容,然后找到文件和目录结构,根据数据文件号,直接将文件抽取出来。因此,使用AMDU一般要满足以下三个情况:1.ASM实例无法启动;2.磁盘中的filedirectory信息完整;3.知道数据文件号;这样我们就可以尝试把数据文件直接抽取出来,在本地启动数据库实例,接下来我们来操作下。
1. 关闭ASM实例
[oracle@rac01~]$ sqlplus /nolog
SQL*Plus:Release 11.2.0.4.0 Production on Thu Jul 23 14:33:24 2015
Copyright(c) 1982, 2013, Oracle. All rightsreserved.
SQL>conn / as sysasm
Connected.
SQL>shutdown immediate;
ASMdiskgroups dismounted
ASMinstance shutdown
由于ASM中存放有CRS相关组件,因此CRS集群也被停止,整个数据库无法访问,如下
[oracle@rac01~]$ crs_stat -t
CRS-0184:Cannot communicate with the CRS daemon.
因为ASM实例没有启动,常用的asmcmd也无法访问相关的信息,如下
[oracle@rac01~]$ asmcmd
Connectedto an idle instance.
ASMCMD>ls
ASMCMD-8102:no connection to Oracle ASM; command requires Oracle ASM to run
过去遇到这种情况,我们只能想方设法的启动ASM实例,不然就只能通过备份文件恢复数据,现在我们可以尝试通过amdu工具,将数据文件直接抽取出来。
2. 利用ODU中的asmcmd工具查看数据文件号
由于ORACLE自带的ASMCMD工具无法使用,这时候我们就要借助牛人开发的ODU工具进行查看了,ODU是技术达人开发的一块收费的数据抽取工具(http://www.oracleodu.com/cn/download),我们只需要下载个试用版,用于查询下就行了。
[oracle@rac01~]$ tar -xvf odu_trial_413_linux_x86.tar
odu/
odu/asmdisk.txt
odu/config.txt
odu/control.txt
odu/data/
odu/odu
解压后,我门只需要对asmdisk.txt这个配置文件进行下设置,只需要设置磁盘号和路径,我们可以通过kfod di=all得到,如下;
[oracle@rac01odu]$ kfod di=all
--------------------------------------------------------------------------------
Disk Size Path User Group
================================================================================
1: 8189 Mb/dev/asm-disk1 oracle dba
2: 8189 Mb/dev/asm-disk2 oracle dba
3: 509 Mb/dev/asm-disk3 oracle dba
KFOD-00313:No ASM instances available. CSS group services were successfully initilized bykgxgncin
KFOD-00311:Error scanning device /dev/asm
ORA-15059:invalid device type for ASM disk
Linux-x86_64Error: 16384: Unknown system error
Additionalinformation: 42
[oracle@rac01odu]$ more asmdisk.txt
#disk_no disk_path group_name meta_block_size ausize disk_size header_offset
1 /dev/asm-disk1
2 /dev/asm-disk2
3 /dev/asm-disk3
进入odu的asmcmd工具,具体操作如下:
[oracle@rac01odu]$ ./odu
OracleData Unloader trial version 4.1.3
Copyright(c) 2008,2009,2010,2011 XiongJun. All rights reserved.
grp#dsk# bsize ausize disksize diskname groupname path
-------- ----- ------ -------- --------------- -----------------------------------------------------------
1 0 4096 1024K 8189 DATA_0000 DATA /dev/asm-disk1
1 1 4096 1024K 8189 DATA_0001 DATA /dev/asm-disk2
2 0 4096 1024K 509 ARCH_0000 ARCH /dev/asm-disk3
loadasm disk file 'asmdisk.txt' successful
loadingdefault control file ......
ts# fn rfn bsize blocks bf offset filename
-------- ---- ----- -------- -- ------ --------------------------------------------
loadcontrol file 'control.txt' successful
loadingdictionary data......done
loadingscanned data......done
ODU> asmcmd
Enteringasmcmd module.
ASMCMD>ls
Currentdirectory: <root>
DiskGroup
------------------------------------
DATA
ARCH
至此,我们就进入了ODU的ASMCMD工具中,这个工具的命令数据库自带的有所细微的区别,给演示下我们如何利用它查询数据文件的文件号:
ASMCMD>cd+data
ASMCMD>cd ORCL
Currentdirectory: +DATA/ORCL
ASMCMD>ls
Currentdirectory: +DATA/ORCL/DATAFILE
Name
----------------------------------------
SYSTEM.256.883666707
SYSAUX.257.883666707
UNDOTBS1.258.883666707
USERS.259.883666707
UNDOTBS2.267.883667019
ZLTOOLSTBS.273.884105179
zltoolstbs.dbf =>+DATA.273.884105179
ZL9BASEITEM.274.884105185
zl9baseitem.dbf =>+DATA.274.884105185
ZL9PATIENT.275.884105191
zl9patient.dbf =>+DATA.275.884105191
ZL9EXPENSE.276.884105193
zl9expense.dbf =>+DATA.276.884105193
ZL9MEDLST.277.884105197
zl9medlst.dbf =>+DATA.277.884105197
ZL9DUEREC.278.884105203
zl9duerec.dbf =>+DATA.278.884105203
ZL9CISREC.279.884105207
zl9cisrec.dbf => +DATA.279.884105207
ASM实例中所有文件,都有自己的文件号,如上面每个文件的后缀中的数字,后续我们就根据文件号,利用amdu抽取出数据文件。
3. 利用amdu抽取数据文件
这里我们尝试控制文件文件抽取出来,让数据库能够启动到mount状态
ASMCMD>cdcontrolfile
Currentdirectory: +DATA/ORCL/CONTROLFILE
ASMCMD>ls
Currentdirectory: +DATA/ORCL/CONTROLFILE
Name
----------------------------------------
Current.261.883666805
Current.260.883666805
这里我们就看到了,控制文件的文件号是260和261,接下来通过amdu进行数据抽取
[oracle@rac01~]$ amdu -diskstring '/dev/asm*' -extract data.260
amdu_2015_07_23_16_15_50/
[oracle@rac01~]$ amdu -diskstring '/dev/asm*' -extract data.261
amdu_2015_07_23_16_16_01/
可以看到,生成了2个文件夹,进入其中的一个
[oracle@rac01~]$ cd amdu_2015_07_23_16_15_50/
[oracle@rac01amdu_2015_07_23_16_15_50]$ ls
DATA_260.f report.txt
AMDU抽取生成数据文件的格式为“磁盘组+数据文件号”,并且加后缀名为点f,如这里生成的文件就是DATA_260.f,这就是抽取的控制文件,我们可以通过mv对这些文件进行移动并重命名,移动为/u01/app/oracle/controlfile01.ctl如下
[oracle@rac01~]$mv /home/oracle/amdu_2015_07_23_16_15_50/DATA_260.f/u01/app/oracle/controlfile01.ctl
接下来我们对系统自带的pfile文件(一般在/u01/app/oracle/admin/orcl/pfile文件夹中)进行编写,修改里面的控制文件的路径,如下
control_files=("+DATA/orcl/controlfile/current.261.883666805","+DATA/orcl/controlfile/current.260.883666805")
cluster_database=true
修改为:
control_files=("/u01/app/oracle/controlfile01.ctl")
cluster_database=false
启动到mount状态
SQL>startup mount pfile='/u01/app/oracle/admin/orcl/pfile/init.ora'
ORACLEinstance started.
TotalSystem Global Area 772472832 bytes
FixedSize 2257232 bytes
VariableSize 566234800 bytes
DatabaseBuffers 201326592 bytes
RedoBuffers 2654208 bytes
Databasemounted.
SQL>
后面我们就可以用类似的方法,通过amdu把其他剩余的所有数据文件、REDO日志文件等抽取出来,然后将控制文件中的路径作调整(可以通过重建控制文件的方式),就是一个简单的冷备恢复,把数据库脱离ASM重新启动起来,由于篇幅限制这里就不再演示了。
6.总结
ASM作为ORACLE推崇的数据存储方式,在性能、安全、管理上都提供了极大的方便,但是由于其高密闭的封装,导致一旦出现问题,DBA对其往往束手无策。今天我们分别介绍了几个日常大家不怎么接触的数据库自带的ASM维护小工具,可以从内部深探ASM的架构,为我们在极端情况下进行一些拯救性的操作提供了方法,请有兴趣的同事能够尝试学习研究,能够有效的掌握这些工具的使用。
参考文档:ASM tools used by Support :KFOD, KFED, AMDU (文档 ID 1485597.1)