Skip to content

MDATA - 3. page

从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.

从ASM直接读取数据的研究思考

从ASM直接读取数据分为2种情况考虑

1.直接读取datafile文件
2.直接读取datafile中的objects

直接读取datafile文件的原理,目前MDATA和AMDU均实现了此功能

先从第一种从ASM中直接读取datafile的情况,这种case也是比较简单的。Datafile在文件系统中的单位是8k块为单位,在从数据字典中读取对象时候从对应的块地址可以直接读文件获取,因此文件系统中用mdata,dul等工具恢复对象的实现方式上虽然复杂,但是相比asm要简单不少,ASM也是一种文件系统,只是是封装的文件系统,普通的操作系统不能直接访问asm中的对象,必须通过asm,asm中构成asm文件的单位是au,因此datafile也是由多个au组成,而au是根据asm的条带大小和条带深度分部的,datafile只能存放于ASM 的DG中,不能跨DG,每个DG由1个或者多个disk组成,在DG中组成datafile的au是根据条带算法分部在dg里面的多个盘里的,因此需要从asm中恢复出datafile,需要知道au的分部情况,每个datafile包含au的地址范围。而ASM中dg的file directory(文件分配表)则可以读到此信息,因此只要找到对应文件的file dir就能找到对应asm file的au分布信息。有兴趣可以研究下asm的1号文件,每个文件在1号文件中都有4k的au 分布信息,1号文件至少为2个AU,海波写过一篇文章专门介绍过用c语言实现从asm中读取datafile,当然我看到的版本只处理30M au分布信息的file diretory(第一个AU),如果文件number超过256则需要在下一个au中读取相关文件的au分布信息。到这里恢复datafile已经不是什么大问题。

直接读取datafile中的objects的原理思路

这个会比较麻烦,用一句话提示吧,数据字典固定同时构建一份au分布信息表类似X$KFFXP。

使用MDATA恢复drop的对象

创建测试表,同时drop对应测试表,模拟drop table的案例

SQL> create table mdata_test ( a number,b varchar2(10),c nvarchar2(30),d varchar2(20),e date);

Table created.

SQL> insert into mdata_test select rownum,lpad('x',10),'NC测试' || rownum, 'ZHS测试'|| rownum,sysdate+dbms_random.value(0,100) from dba_objects where rownum< =20000;

20000 rows created.

SQL> commit;

Commit complete.

SQL> select object_id,object_name from dba_objects where object_name='MDATA_TEST';

 OBJECT_ID
----------
OBJECT_NAME
--------------------------------------------------------------------------------
     51997
MDATA_TEST


SQL> drop table mdata_test_bak purge;

Table dropped.

SQL> create table mdata_test_bak as select * from mdata_test;

Table created.

SQL> drop table mdata_test purge;

Table dropped.

SQL> quit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

加载mdata

olm@hc10 /home/oracle/MDATA$ ./MDATA.sh

MDATA for oracle 9I,10G,11G, release 3.0.3
(@)copyright LUDATOU,HC all rights reserved.

Web:www.ludatou.com,www.hcdba.com
Email:feigigi@qq.com,564439763@qq.com
QQ group:66809572

loading default config.......
load config file 'config.txt' successful
loading default asm disk file ......
start loading default control file ......
load control file 'control.txt' successful
load BOOTSTRAP$ success.
load TAB$ success.
load COL$ success.
load OBJ$ success.
load USER$ success.
load PROPS$ success.
load TABPART$ success.
load TABSUBPART$ success.
load IND$ success.

先对被drop的表所在tablespace进行scan做操

MDATA>scan extent tablespace 4
scan extent start: Tue Jan 20 22:23:10 CST 2015
scanning extent...
scanning extent finished.
scan extent completed: Tue Jan 20 22:23:12 CST 2015

接着通过drop恢复的语法对drop的表进行恢复

MDATA>unload object 51994 column number varchar2 nvarchar2 varchar2 date
Unloading Object,object ID: 51994,  Cluster: 0
-1 rows unloaded
MDATA>unload object 51997 column number varchar2 nvarchar2 varchar2 date
Unloading Object,object ID: 51997,  Cluster: 0
20000 rows unloaded
MDATA>exit

恢复结束后,切换到mdata的ddl目录,通过生成的ddl语句在instance中重建该table

olm@hc10 /home/oracle/MDATA$ cd ddl
olm@hc10 /home/oracle/MDATA/ddl$ sqlplus hc/hc

SQL*Plus: Release 10.2.0.5.0 - Production on Tue Jan 20 22:23:56 2015

Copyright (c) 1982, 2010, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> host ls
appclean.sql  MDATA_0000051979.sql  MDATA_0000051994.sql  SYS.IND.sql    SYS.TABPART.sql     SYS.USER.sql
app.sql       MDATA_0000051981.sql  MDATA_0000051997.sql  SYS.PROPS.sql  SYS.TABSUBPART.sql

SQL> host ls -lrt
total 44
-rw-r--r-- 1 oracle oinstall  281 Oct  6 16:57 appclean.sql
-rw-r--r-- 1 oracle oinstall 1909 Oct  6 17:44 app.sql
-rw-r--r-- 1 oracle oinstall  249 Jan 20 16:22 MDATA_0000051979.sql
-rw-r--r-- 1 oracle oinstall  249 Jan 20 16:53 MDATA_0000051981.sql
-rw-r--r-- 1 oracle oinstall  249 Jan 20 22:20 MDATA_0000051994.sql
-rw-r--r-- 1 oracle oinstall 1328 Jan 20 22:23 SYS.USER.sql
-rw-r--r-- 1 oracle oinstall  193 Jan 20 22:23 SYS.PROPS.sql
-rw-r--r-- 1 oracle oinstall 1367 Jan 20 22:23 SYS.TABSUBPART.sql
-rw-r--r-- 1 oracle oinstall 1277 Jan 20 22:23 SYS.TABPART.sql
-rw-r--r-- 1 oracle oinstall 1761 Jan 20 22:23 SYS.IND.sql
-rw-r--r-- 1 oracle oinstall  249 Jan 20 22:23 MDATA_0000051997.sql

SQL> @MDATA_0000051997.sql

Table created.

SQL> quit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

接着切换到mdata目录下的data目录,通过sqlldr工具,对导出来的数据恢复到被刚创建的表中去

olm@hc10 /home/oracle/MDATA/ddl$ cd ../data
olm@hc10 /home/oracle/MDATA/data$ sqlldr "'hc/hc' control=MDATA_0000051997.ctl"

SQL*Loader: Release 10.2.0.5.0 - Production on Tue Jan 20 22:24:51 2015

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Commit point reached - logical record count 6502
Commit point reached - logical record count 13004
Commit point reached - logical record count 19506
Commit point reached - logical record count 20000

导入完成后,登入数据库验证恢复

olm@hc10 /home/oracle/MDATA/data$ sqlplus / as sysdba;

SQL*Plus: Release 10.2.0.5.0 - Production on Tue Jan 20 22:25:08 2015

Copyright (c) 1982, 2010, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> conn hc/hc
Connected.
SQL> select * from mdata_test_bak minus select * from MDATA_0000051997;

no rows selected

至此drop的恢复完成。

使用mdata恢复drop的表需要知道该对象的object id,同时需要知道该表的表结构(字段,字段属性)

使用MDATA恢复Oracle中truncate的数据

创建测试表并模拟truncate的场景:

SQL> create table trutab as select * from t1;

Table created.

SQL> select count(*) from trutab;

  COUNT(*)
----------
     10000

SQL> commit
  2  ;

Commit complete.

SQL>
SQL>
SQL> truncate table trutab;

Table truncated.

SQL> select count(*) from trutab;

  COUNT(*)
----------
         0

SQL> select segment_name,tablespace_name from dba_segments where segment_name='TRUTAB' and owner='LUDA';

SEGMENT_NAME                             TABLESPACE_NAME
---------------------------------------- ----------------------------------------
TRUTAB                                   USERS

加载mdata

MDATA>reload dict
Start reload dict,Mon Jan 12 13:46:45 CST 2015
load BOOTSTRAP$ success.
load TAB$ success.
load COL$ success.
load OBJ$ success.
load USER$ success.
load PROPS$ success.
load TABPART$ success.
load TABSUBPART$ success.
load IND$ success.
End reload dict,Mon Jan 12 13:46:51 CST 2015,reload success.
MDATA>list table luda
OWNER                         TABLE
---------------               --------------
LUDA                          T2
LUDA                          T1
LUDA                          TRUTAB
owner:LUDA has 3  rows selected.

通过scan的方式扫描被truncate的表所在的tablespace

MDATA>unload table luda.trutab
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@f0ca71,getFileid:4,getBlockid:195
0 rows unloaded
MDATA>scan extent tablespace 4 datafile 4
scan extent start: Mon Jan 12 13:48:14 CST 2015
scanning extent...
scanning extent finished.
scan extent completed: Mon Jan 12 13:48:15 CST 2015
MDATA>unload table luda.trutab
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@26e5d8,getFileid:4,getBlockid:195
0 rows unloaded
MDATA>scan extent tablespace 4 datafile 4 auto
scan extent [tablespace <ts #> [datafile <rfile #>] ] [object <data_object_data>] parallel [parallel_degree]

scan做操完成后,对该表执行unload操作

MDATA>unload table luda.trutab object auto
Auto mode truncated table.
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@cd1365,getFileid:4,getBlockid:195
10000 rows unloaded
MDATA>

导出恢复的数据后,通过sqlload工具将导出的数据恢复到trutab表中:

[oracle@DB01 data]$ sqlldr luda/luda control=LUDA.TRUTAB.ctl

SQL*Loader: Release 10.2.0.4.0 - Production on Mon Jan 12 13:52:44 2015

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Commit point reached - logical record count 2501
Commit point reached - logical record count 5002
Commit point reached - logical record count 7503
Commit point reached - logical record count 10000

恢复完成后验证恢复:

[oracle@DB01 data]$ sqlplus '/as sysdba'

SQL*Plus: Release 10.2.0.4.0 - Production on Mon Jan 12 13:52:54 2015

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select count(*) from luda.trutab;

  COUNT(*)
----------
     10000

至此truncate的恢复已经完成。提示一个小技巧,mdata的scan功能支持并行模式