Skip to content

由繁化简:快速解析asm disk header结构

在asm instance crash,特定的一些场景下无法启动asm,为了恢复数据或者其他需要则需要直接从disk中读取数据,此时asm disk的number则为必需获取的信息,特别是在一些不规范的disk命名环境,因此需要用kfed读取磁盘头依次获取信息。

在disk中

kfdhdb.dsknum 代表磁盘号
kfdhdb.grptyp 代表磁盘类型
kfdhdb.dskname 代表磁盘名字
kfdhdb.grpname 代表磁盘所在asm diskgroup 名字
kfdhdb.blksize 代表磁盘的块单元大小
kfdhdb.dsksize 代表磁盘的全部大小

因此识别asm disk所属的asm diskgroup number以及asm disk number只需要在用kfed read asm disk时候加上dsknum以及grpname来区分即可
例如

 kfed read /dev/oracleasm/disks/VOL01   | grep dsknum

或者写成批量处理的脚本,效率会更高。
通过此种方式找到0号磁盘的情况下,通过识别2号au的的file 1,就可以找到想恢复的文件了。

案例:

[oracle@oradb bin]$ kfed read /dev/oracleasm/disks/VOL01
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: T=0 NUMB=0x0
kfbh.block.obj:              2147483648 ; 0x008: TYPE=0x8 NUMB=0x0
kfbh.check:                  3091711072 ; 0x00c: 0xb847c460
kfbh.fcn.base:                        0 ; 0x010: 0x00000000
kfbh.fcn.wrap:                        0 ; 0x014: 0x00000000
kfbh.spare1:                          0 ; 0x018: 0x00000000
kfbh.spare2:                          0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr:    ORCLDISKVOL01 ; 0x000: length=13
kfdhdb.driver.reserved[0]:    810307414 ; 0x008: 0x304c4f56
kfdhdb.driver.reserved[1]:           49 ; 0x00c: 0x00000031
kfdhdb.driver.reserved[2]:            0 ; 0x010: 0x00000000
kfdhdb.driver.reserved[3]:            0 ; 0x014: 0x00000000
kfdhdb.driver.reserved[4]:            0 ; 0x018: 0x00000000
kfdhdb.driver.reserved[5]:            0 ; 0x01c: 0x00000000
kfdhdb.compat:                168820736 ; 0x020: 0x0a100000
kfdhdb.dsknum:                        0 ; 0x024: 0x0000                  //这里是磁盘号。
kfdhdb.grptyp:                        3 ; 0x026: KFDGTP_HIGH             //这里是磁盘类型。
kfdhdb.hdrsts:                        3 ; 0x027: KFDHDR_MEMBER
kfdhdb.dskname:                   VOL01 ; 0x028: length=5                //磁盘名称。
kfdhdb.grpname:                    DATA ; 0x048: length=4                //磁盘所属的磁盘组。
kfdhdb.fgname:                    VOL01 ; 0x068: length=5
kfdhdb.capname:                         ; 0x088: length=0
kfdhdb.crestmp.hi:             32942006 ; 0x0a8: HOUR=0x16 DAYS=0x1d MNTH=0x9 YEAR=0x7da
kfdhdb.crestmp.lo:            449689600 ; 0x0ac: USEC=0x0 MSEC=0x36e SECS=0x2c MINS=0x6
kfdhdb.mntstmp.hi:             32942646 ; 0x0b0: HOUR=0x16 DAYS=0x11 MNTH=0xa YEAR=0x7da
kfdhdb.mntstmp.lo:           1573951488 ; 0x0b4: USEC=0x0 MSEC=0x26 SECS=0x1d MINS=0x17
kfdhdb.secsize:                     512 ; 0x0b8: 0x0200
kfdhdb.blksize:                    4096 ; 0x0ba: 0x1000                    //这里指每个block的大小。
kfdhdb.ausize:                  1048576 ; 0x0bc: 0x00100000
kfdhdb.mfact:                    113792 ; 0x0c0: 0x0001bc80
kfdhdb.dsksize:                   10236 ; 0x0c4: 0x000027fc             //这里是磁盘的大小。
kfdhdb.pmcnt:                         2 ; 0x0c8: 0x00000002
kfdhdb.fstlocn:                       1 ; 0x0cc: 0x00000001
kfdhdb.altlocn:                       2 ; 0x0d0: 0x00000002
kfdhdb.f1b1locn:                      2 ; 0x0d4: 0x00000002
kfdhdb.redomirrors[0]:                0 ; 0x0d8: 0x0000
kfdhdb.redomirrors[1]:                0 ; 0x0da: 0x0000
kfdhdb.redomirrors[2]:                0 ; 0x0dc: 0x0000
kfdhdb.redomirrors[3]:                0 ; 0x0de: 0x0000
kfdhdb.dbcompat:              168820736 ; 0x0e0: 0x0a100000
kfdhdb.grpstmp.hi:             32942006 ; 0x0e4: HOUR=0x16 DAYS=0x1d MNTH=0x9 YEAR=0x7da
kfdhdb.grpstmp.lo:            448217088 ; 0x0e8: USEC=0x0 MSEC=0x1d0 SECS=0x2b MINS=0x6
kfdhdb.ub4spare[0]:                   0 ; 0x0ec: 0x00000000
kfdhdb.ub4spare[1]:                   0 ; 0x0f0: 0x00000000
kfdhdb.ub4spare[2]:                   0 ; 0x0f4: 0x00000000
kfdhdb.ub4spare[3]:                   0 ; 0x0f8: 0x00000000
kfdhdb.ub4spare[4]:                   0 ; 0x0fc: 0x00000000
kfdhdb.ub4spare[5]:                   0 ; 0x100: 0x00000000
kfdhdb.ub4spare[6]:                   0 ; 0x104: 0x00000000
kfdhdb.ub4spare[7]:                   0 ; 0x108: 0x00000000
kfdhdb.ub4spare[8]:                   0 ; 0x10c: 0x00000000
kfdhdb.ub4spare[9]:                   0 ; 0x110: 0x00000000
kfdhdb.ub4spare[10]:                  0 ; 0x114: 0x00000000
kfdhdb.ub4spare[11]:                  0 ; 0x118: 0x00000000
kfdhdb.ub4spare[12]:                  0 ; 0x11c: 0x00000000
kfdhdb.ub4spare[13]:                  0 ; 0x120: 0x00000000
kfdhdb.ub4spare[14]:                  0 ; 0x124: 0x00000000
kfdhdb.ub4spare[15]:                  0 ; 0x128: 0x00000000
kfdhdb.ub4spare[16]:                  0 ; 0x12c: 0x00000000
kfdhdb.ub4spare[17]:                  0 ; 0x130: 0x00000000
kfdhdb.ub4spare[18]:                  0 ; 0x134: 0x00000000
kfdhdb.ub4spare[19]:                  0 ; 0x138: 0x00000000
kfdhdb.ub4spare[20]:                  0 ; 0x13c: 0x00000000
kfdhdb.ub4spare[21]:                  0 ; 0x140: 0x00000000
kfdhdb.ub4spare[22]:                  0 ; 0x144: 0x00000000
kfdhdb.ub4spare[23]:                  0 ; 0x148: 0x00000000
kfdhdb.ub4spare[24]:                  0 ; 0x14c: 0x00000000
kfdhdb.ub4spare[25]:                  0 ; 0x150: 0x00000000
kfdhdb.ub4spare[26]:                  0 ; 0x154: 0x00000000
kfdhdb.ub4spare[27]:                  0 ; 0x158: 0x00000000
kfdhdb.ub4spare[28]:                  0 ; 0x15c: 0x00000000
kfdhdb.ub4spare[29]:                  0 ; 0x160: 0x00000000
kfdhdb.ub4spare[30]:                  0 ; 0x164: 0x00000000
kfdhdb.ub4spare[31]:                  0 ; 0x168: 0x00000000
kfdhdb.ub4spare[32]:                  0 ; 0x16c: 0x00000000
kfdhdb.ub4spare[33]:                  0 ; 0x170: 0x00000000
kfdhdb.ub4spare[34]:                  0 ; 0x174: 0x00000000
kfdhdb.ub4spare[35]:                  0 ; 0x178: 0x00000000
kfdhdb.ub4spare[36]:                  0 ; 0x17c: 0x00000000
kfdhdb.ub4spare[37]:                  0 ; 0x180: 0x00000000
kfdhdb.ub4spare[38]:                  0 ; 0x184: 0x00000000
kfdhdb.ub4spare[39]:                  0 ; 0x188: 0x00000000
kfdhdb.ub4spare[40]:                  0 ; 0x18c: 0x00000000
kfdhdb.ub4spare[41]:                  0 ; 0x190: 0x00000000
kfdhdb.ub4spare[42]:                  0 ; 0x194: 0x00000000
kfdhdb.ub4spare[43]:                  0 ; 0x198: 0x00000000
kfdhdb.ub4spare[44]:                  0 ; 0x19c: 0x00000000
......

通过x$KFDAT确认asm file的au信息

X$KFDAT (metadata, disk-to-AU mapping table)

该视图的结构图,以及字段含义
x$kfdat

example:

查找spfile的au信息:

sys@+ASM1> select GROUP_KFDAT,NUMBER_KFDAT,AUNUM_KFDAT from x$kfdat where
   fnum_kfdat=(select file_number from v$asm_alias where name='spfiletest1.ora');
GROUP_KFDAT NUMBER_KFDAT AUNUM_KFDAT
----------- ------------ -----------
          1            3         101
          1           20         379


通过以上可以知道,spfile存在在1号diskgroup的3号磁盘和20号磁盘的2个au,文件所在位置在3号磁盘的相对硬盘第一个au的位置为101号,在20号磁盘相对的位置为379号.x$kfdat的AUNUM_KFDAT字段与x$kffxp视图中的au_kffxp为同一信息.我的diskgroup的为normal冗余模式,所以3.101au和20.379au为mirror关系,可以通过x$kffxp.xnum_kffxp验证,mirror的2个au相关的extent number 一致.x$kfdat与x$kffxp有些字段是关联的.

在12c的asm中,oracle在asmcmd中新增2个命令来确认asm file相关au信息,分别为mapextent以及map au:

example:

ASMCMD>  mapextent '+ORCL_MYTEST/ORCL/DATAFILE/mytest.256.8332901607' 1
Disk_Num         AU      Extent_Size
1                211     1
0                211     1

ASMCMD> mapau
usage: mapau [--suppressheader] <dg number> <disk number> <au>
help:  help mapau
ASMCMD> mapau 1 1 107
File_Num         Extent          Extent_Set
261              1273            636

从ASM DISKS中定位表行在系统DISK中的物理偏移位置

该部分也是承接从ASM直接读取数据的研究思考,主要是根据asm实例中的x$kffxp视图确定au的位置。

首先准备测试环境的脚本,数据块大小默认为8l:

SQL script to create tablespace
col   "File name" format a60
col "Tablespace name" format a20
--创建表空间:
connect sys/ludatou as sysdba
create tablespace test  datafile '+TEST' size 5m;
grant dba to luda;
--创建测试表:
connect luda/luda
create table luda_tab (n number, name varchar2(16)) tablespace test;
insert into luda_tab values (1, 'ASM_TEST');
commit;
select ROWID, NAME from luda_tab;

通过以上查询可以知道luda_tab的行块所在位置为数据文件的133号数据块。

SQL> select ROWID from luda_tab;

ROWID
------------------
AAAXgDAAHAAAACFAAA

SQL> select DBMS_ROWID.ROWID_BLOCK_NUMBER('AAAXgDAAHAAAACFAAA') "Block number" from DUAL;

Block number
------------
133

 
数据库块大小为8k

SQL> show parameter db_block_size

NAME                     TYPE     VALUE
------------------------------------ ----------- ------------------------------
db_block_size                 integer     8192

TEST表空间的数据文件ASM file number为256

SQL> select f.FILE#, f.NAME "File name", t.NAME "Tablespace name" from V$DATAFILE f, V$TABLESPACE t where t.NAME='TEST' and f.TS# = t.TS#;

FILE# File name                                       Tablespace name
---------- -------------------------------------------------- ---------------
7 +TEST/racnode1/datafile/test.256.852905863          TEST

TEST表空间所在的diskgroup号为4

SQL> select GROUP_NUMBER from V$ASM_DISKGROUP where NAME='TEST';

GROUP_NUMBER
------------
       4

4号diskgroup的au size为1M

SQL> select VALUE from V$ASM_ATTRIBUTE where NAME='au_size' and GROUP_NUMBER=4;

VALUE
------------------------------------------------------------------------------
1048576

Diskgroup 4的磁盘信息

SQL> select GROUP_NUMBER, DISK_NUMBER, NAME, path from V$ASM_DISK  where  GROUP_NUMBER=4;
GROUP_NUMBER DISK_NUMBER NAME                PATH
------------ ----------- ------------------------------ ------------------------------
       4           0 TEST_0000            /dev/asm_disk_1
       4           1 TEST_0001            /dev/asm_disk_2

AU分部与OS磁盘的对应信息

select PXN_KFFXP, -- physical extent number
  XNUM_KFFXP, -- virtual extent number
  DISK_KFFXP, -- disk number
  AU_KFFXP    -- allocation unit number
from X$KFFXP
where NUMBER_KFFXP=256 -- ASM file 256
AND GROUP_KFFXP=4 -- group number 4
order by 1;

 PXN_KFFXP XNUM_KFFXP DISK_KFFXP   AU_KFFXP
---------- ---------- ---------- ----------
     0        0           0    144
     1        0           1    144
     2        1           1    145
     3        1           0    145
     4        2           0    146
     5        2           1    146
     6        3           1    147
     7        3           0    147
     8        4           0    148
     9        4           1    148
    10        5           1    149
    11        5           0    149

从上面的au分布与os磁盘的对应信息中可以看到256号asm file一共有12个au,au size为1m。由于diskgroup为normal reduncancy模式,所以每一个au在1号磁盘中还有一个镜像,所以一共是12个AU,占用12M。blocksize为8k,所以每个au包含的block数量为1024/8=128个,因为行块所在位置为133号数据块,所以该块应该处于数据文件的第二个au的5(133-128)号8k单元数据块,也就是磁盘/dev/asm_disk_1的145号AU,仔细观察可以发现extent也为对应的号数,由于冗余所以每个extent都有2个。

接下来确认行块在asm_disk_1以及asm_disk_2上

[grid@racnode1 luda]$  strings  /dev/asm_disk_1 | grep ASM_TEST
ASM_TEST
[grid@racnode1 luda]$ strings   /dev/asm_disk_2 | grep ASM_TEST
ASM_TEST

接着使用dd导出磁盘中145号AU的数据

[grid@racnode1 luda]# dd if=/dev/asm_disk_1  bs=1024k count=1 skip=144 of=AU_145.data
1+0 records in
1+0 records out
1048576 bytes (1.0 MB) copied, 0.031567 seconds, 21.3 MB/s
[grid@racnode1 luda]$
[grid@racnode1 luda]$ ls -l AU_145.data
-rw-r--r-- 1 grid oinstall 1048576 Aug 13 22:45 AU_145.data
[grid@racnode1 luda]$

下来就可以从145号au中取出第5个8k块的数据

[grid@racnode1 luda]$ dd if=AU_145.data bs=8k count=1 skip=4 of=block133.data

使用od观察该块即可找到我们想要的数据ASM_TEST。

[grid@racnode1 luda]$ od -c block133.data
0137760 002 301 002  \b   A   S   M   _   T   E   S   T 001 006   i 356Looks good

到此通过AU与OS磁盘的映射关系,验证了可以找到对应行的数据。解释了如何通过asm直接读取object的基本原理。

从ASM DISKS中确定DATAFILE的存储位置

承接ASM恢复时间的研究思考,这里补充从ASM中恢复出数据文件的需要知道细节部分。

首先找到asm的file directory的物理位置:

[oracle@rac1 ~]$ kfed read /dev/raw/raw1 aunum=0 blknum=0
kfbh.endian:                          1 ; 0×000: 0×01
kfbh.hard:                          130 ; 0×001: 0×82
kfbh.type:                            1 ; 0×002: KFBTYP_DISKHEAD
……
kfdhdb.blksize:                    4096 ; 0x0ba: 0×1000
kfdhdb.ausize:                  1048576 ; 0x0bc: 0×00100000
kfdhdb.mfact:                    113792 ; 0x0c0: 0x0001bc80
kfdhdb.dsksize:                    3067 ; 0x0c4: 0x00000bfb
kfdhdb.pmcnt:                         2 ; 0x0c8: 0×00000002
kfdhdb.fstlocn:                       1 ; 0x0cc: 0×00000001
kfdhdb.altlocn:                       2 ; 0x0d0: 0×00000002
kfdhdb.f1b1locn:                      2 ; 0x0d4: 0×00000002

从结果里我们可以看到,file directory的第1个block在AU2上,而从10-12c中的研究分析,ASM的1号文件的file dir的地址都在0号盘的au2上,如果到这里看不懂,研究下asm的metadata文件1-255。

接下来看一下AU2的1号block:

[oracle@rac1 ~]$ kfed read /dev/raw/raw1 aunum=2 blknum=1
kfbh.endian:                          1 ; 0×000: 0×01
kfbh.hard:                          130 ; 0×001: 0×82
kfbh.type:                            4 ; 0×002: KFBTYP_FILEDIR
……
kfffdb.usm:                             ; 0x0a0: length=0
<strong>kfffde[0].xptr.au:                    2 ; 0x4a0: 0×00000002</strong>
kfffde[0].xptr.disk:                  0 ; 0x4a4: 0×0000
kfffde[0].xptr.flags:                 0 ; 0x4a6: L=0 E=0 D=0 C=0 S=0
kfffde[0].xptr.chk:                  40 ; 0x4a7: 0×28
kfffde[1].xptr.au:                    2 ; 0x4a8: 0×00000002
kfffde[1].xptr.disk:                  1 ; 0x4ac: 0×0001
kfffde[1].xptr.flags:                 0 ; 0x4ae: L=0 E=0 D=0 C=0 S=0
kfffde[1].xptr.chk:                  41 ; 0x4af: 0×29
kfffde[2].xptr.au:           4294967294 ; 0x4b0: 0xfffffffe
kfffde[2].xptr.disk:              65534 ; 0x4b4: 0xfffe
kfffde[2].xptr.flags:                 0 ; 0x4b6: L=0 E=0 D=0 C=0 S=0
kfffde[2].xptr.chk:                  42 ; 0x4b7: 0x2a
<strong>kfffde[3].xptr.au:                   93 ; 0x4b8: 0x0000005d</strong>
kfffde[3].xptr.disk:                  0 ; 0x4bc: 0×0000
kfffde[3].xptr.flags:                 0 ; 0x4be: L=0 E=0 D=0 C=0 S=0
kfffde[3].xptr.chk:                 119 ; 0x4bf: 0×77
kfffde[4].xptr.au:                   93 ; 0x4c0: 0x0000005d
kfffde[4].xptr.disk:                  1 ; 0x4c4: 0×0001
kfffde[4].xptr.flags:                 0 ; 0x4c6: L=0 E=0 D=0 C=0 S=0
kfffde[4].xptr.chk:                 118 ; 0x4c7: 0×76
kfffde[5].xptr.au:           4294967294 ; 0x4c8: 0xfffffffe
……
kfffde[39].xptr.disk:             65535 ; 0x5dc: 0xffff

从结果里我们可以知道/dev/raw/raw1只有两个file directory,第一个file directory在AU2上,第二个file directory在AU93上。asm的默认au为1m,每个file的dir大小为4k,所以每个au最多只能存储256个file,asm1-255file为metadata,所以通常的数据库数据文件都在第二个AU后(这里建议关注下,如果datafile所占有的au数量非常多,4k信息装不下怎么办?)。
在这里1M AU的情况下对于第二个file directory而言,datafile 1就在block 0里,datafile 2就在block 1里,依次类推,所以datafile 4在block 3里(au的基础单元为os块4k大小),下来观察这个block:

[oracle@rac1 ~]$ kfed read /dev/raw/raw1 aunum=93 blknum=3
kfbh.endian:                          1 ; 0×000: 0×01
kfbh.hard:                          130 ; 0×001: 0×82
kfbh.type:                            4 ; 0×002: KFBTYP_FILEDIR
kfbh.datfmt:                          1 ; 0×003: 0×01
kfbh.block.blk:                     259 ; 0×004: T=0 NUMB=0×103
kfbh.block.obj:                       1 ; 0×008: TYPE=0×0 NUMB=0×1
……
kfffdb.spare[15]:                     0 ; 0x09c: 0×00000000
kfffdb.usm:                             ; 0x0a0: length=0
kfffde[0].xptr.au:                  211 ; 0x4a0: 0×000000d3
kfffde[0].xptr.disk:                  1 ; 0x4a4: 0×0001
kfffde[0].xptr.flags:                 0 ; 0x4a6: L=0 E=0 D=0 C=0 S=0
kfffde[0].xptr.chk:                 106 ; 0x4a7: 0x6a
kfffde[1].xptr.au:                  211 ; 0x4a0: 0×000000d3
kfffde[1].xptr.disk:                  0 ; 0x4ac: 0×0000
kfffde[1].xptr.flags:                 0 ; 0x4ae: L=0 E=0 D=0 C=0 S=0
kfffde[1].xptr.chk:                 107 ; 0x4af: 0x6b
kfffde[2].xptr.au:                  212 ; 0x4b0: 0×000000d4
kfffde[2].xptr.disk:                  0 ; 0x4b4: 0×0000
kfffde[2].xptr.flags:                 0 ; 0x4b6: L=0 E=0 D=0 C=0 S=0
kfffde[2].xptr.chk:                 106 ; 0x4b7: 0x6a
kfffde[3].xptr.au:                  212 ; 0x4b0: 0×000000d4
kfffde[3].xptr.disk:                  1 ; 0x4bc: 0×0001
kfffde[3].xptr.flags:                 0 ; 0x4be: L=0 E=0 D=0 C=0 S=0
kfffde[3].xptr.chk:                 107 ; 0x4bf: 0x6b
kfffde[4].xptr.au:                  213 ; 0x4c0: 0×000000d5
kfffde[4].xptr.disk:                  1 ; 0x4c4: 0×0001
kfffde[4].xptr.flags:                 0 ; 0x4c6: L=0 E=0 D=0 C=0 S=0
kfffde[4].xptr.chk:                 108 ; 0x4c7: 0x6c
kfffde[5].xptr.au:                  213 ; 0x4c0: 0×000000d5
kfffde[5].xptr.disk:                  0 ; 0x4cc: 0×0000
kfffde[5].xptr.flags:                 0 ; 0x4ce: L=0 E=0 D=0 C=0 S=0
kfffde[5].xptr.chk:                 109 ; 0x4cf: 0x6d
kfffde[6].xptr.au:                  214 ; 0x4d0: 0×000000d6
kfffde[6].xptr.disk:                  0 ; 0x4d4: 0×0000
kfffde[6].xptr.flags:                 0 ; 0x4d6: L=0 E=0 D=0 C=0 S=0
kfffde[6].xptr.chk:                 108 ; 0x4d7: 0x6c
kfffde[7].xptr.au:                  214 ; 0x4d0: 0×000000d6
kfffde[7].xptr.disk:                  1 ; 0x4dc: 0×0001
kfffde[7].xptr.flags:                 0 ; 0x4de: L=0 E=0 D=0 C=0 S=0
kfffde[12].xptr.au:          4294967295 ; 0×500: 0xffffffff
kfffde[12].xptr.disk:             65535 ; 0×504: 0xffff
……
kfffde[23].xptr.au:          4294967295 ; 0×558: 0xffffffff
kfffde[23].xptr.disk:             65535 ; 0x55c: 0xffff

根据上面的信息可以发现datafile 4在/dev/raw/raw1上的物理存储位置可以看到datafile 4一共占了6个AU,分别是AU211,AU212,AU213,AU214.