DUL is Life

系统情况如下:
1. Oracle版本是10.2.0.1,操作系统是Linux x86-64
2. 数据文件存储在OCFS2上
3. 需要恢复的表是普通表
4. 未知故障引起online日志文件损坏,使用隐含参数启动数据库之后,已经将大部分表通过exp导出到新库中,但是个别表无论是使用exp还是使用select都会报ORA-01555 snapshot too old错误,很明显回滚段数据存在不一致。

由于只有小部分数据无法导出,因此决定使用DUL直接从数据块中读取数据。

对于OCFS2文件系统无需特殊处理,在control.dul中按照路径写入系统表空间数据文件和故障表所处的用户表空间数据文件即可。

由于system表空间正常,数据字典都存在,因此unload table的过程很简单,bootstrap之后直接unload table即可。

这次unload出的表中最多记录数为580万多。

最后将生成的dat文件通过SQL Loader加载回新建的数据库中,对于中文的处理需要将操作系统NLS_LANG环境参数设置为跟数据库字符集相同。

本来这次想测试老熊的ODU执行效率,很可惜,在执行linux版本的odu时报无法找到某lib,因此作罢(由于时间充裕,在等待后续处理的过程中,将系统表空间文件和其中一个数据文件ftp到windows中,然后使用windows版本的odu测试了一下,unload table功能完全正常)。

How to recover data with DUL after datafile offline

系统情况如下:
1. Oracle版本是10.2.0.3,操作系统是HP-UX PA RISC 64
2. 数据文件存储在裸设备上
3. 需要恢复的表是分区表
4. 在调整存储硬件的时候没有关闭数据库,由于某些不可预知的故障导致某些用户表空间的数据文件write error,然后自动offline了
5. 数据库确实是归档模式,但是有一个每小时删除一次归档的crontab,删除了重新online数据文件需要的所有归档
6. 保留了之前建表时的create脚本

恢复过程如下:
1. DUL使用的init.dul内容:

CONTROL_FILE = control.dul
DB_BLOCK_SIZE = 16384

2. DUL使用的control.dul文件内容

0 1 /somepath/rsystem_01_8g blocks 524161
22 128 /somepath/r_data07_01_32g blocks 2096641
22 134 /somepath/r_data07_02_32g blocks 2096641 
22 141 /somepath/r_data07_03_32g blocks 2096641
22 147 /somepath/r_data07_04_32g blocks 2096641
22 153 /somepath/r_data07_05_32g blocks 2096641
22 159 /somepath/r_data07_06_32g blocks 2096641
22 165 /somepath/r_data07_07_32g blocks 2096641
22 171 /somepath/r_data07_08_32g blocks 2096641
22 177 /somepath/r_data07_09_32g blocks 2096641
22 183 /somepath/r_data07_10_32g blocks 2096641

在HP-UX PA RISC中,虽然数据文件是存储在裸设备中的,但是仍然不需要设置需要跳过的字节,跟AIX中DUL相比较,缺少了第4位的4096。

3. 登录DUL

DUL> bootstrap;
DUL> unload table user_name.table_name;

由于system表空间正常,数据字典都存在,因此unload table的过程很简单,并且对于分区表(复合分区表)DUL也是完全胜任的,只需要指定table name,就会自动unload出该表的所有分区以及子分区的数据。

这次DUL大概花了4个小时,unload出120G左右的数据。数据库字符集为ZHS16GBK,字段内容包含中文内容,DUL可以unload出正确的字符。

4. 重新创建表结构,将DUL卸载出的数据再sqlldr到表中。
此步操作在处理位于offline表空间中的表分区时碰到了ORA-14117错误,详细参看How to resolve ORA-14117: partition resides in offlined tablespace文章。

How to recover data with DUL after table dropped

首先系统情况如下:
1. Oracle版本是9.2.0.8,操作系统是IBM AIX
2. 数据文件存储在裸设备上
3. 被删除的表是分区表
4. 数据库非归档,没有任何备份
5. 在误drop table以后,立刻将包含该表的表空间offline了,并且保留了当时的current online redo文件
6. 保留了之前建表时的create脚本

恢复过程如下:
1. DUL使用的init.dul内容:

osd_big_endian_flag=true
osd_dba_file_bits=10
osd_c_struct_alignment=32
osd_file_leader_size=1
osd_word_size=32

control_file=/tmp/control.dul
db_block_size=8192
compatible=9

2. DUL使用的control.dul文件内容

58 58 /somepath/rawdevice1 4096 blocks 4095873
58 110 /somepath/rawdevice2 4096 blocks 639873
58 126 /somepath/rawdevice3 4096 blocks 255873

第1位是TS#,第2位是RFILE#,第3位是数据文件全路径,第4位是在AIX系统中裸设备需要跳过的字节,第5位是数据文件的真实block数(从v$datafile中以size/db_block_size获得)。

第4,5两位在数据文件为裸设备的情况下才需要,否则在scan database时扫描到裸设备结尾的时候,将可能报错。

3. 通过logminer得到被drop掉的表分区的data object id(因为被drop了,因此无法从system数据字典中获得)。如何使用Logminer不在本文范围内。
比如最后看到SQL_REDO字段的结果是类似于这样的输出:

delete from "SYS"."OBJ$" where "OBJ#" = '25514' and "DATAOBJ#" = '25514' ......

那么就可以知道data object id = 25514

4. 登录DUL

DUL> scan database;
DUL> unload table table_name partition(p_name) 
(col_name1 VARCHAR2(6),col_name2 VARCHAR2(17),col_name3 VARCHAR2(3).....) 
storage (dataobjno 25514);

这里的table_name和p_name其实都可以随便写,甚至不写partition也可以,最主要的是dataobjno必须正确,就可以unload出表数据。当然还必须有表结构,否则会报告无法在OBJ$中找到相应表。

如果此处没有事先保留的create table脚本,而且也不知道表中有哪些字段,那么工作量就大了。
基本步骤如下:

DUL> alter session set use_scanned_extent_map = true;
DUL> scan database;
DUL> scan tables;

然后在dul.log中查找上面的data object id,找到的话,将会是类似于下面的输出:

UNLOAD TABLE OBJNO25514 ( COL001 VARCHAR2(6), COL002 VARCHAR2(17), COL003 VARCHAR2(3)
        , COL004 VARCHAR2(26), COL005 VARCHAR2(30), COL006 NUMBER, COL007 NUMBER
        , COL008 VARCHAR2(1), COL009 NUMBER, COL010 VARCHAR2(38) )
    STORAGE( DATAOBJNO 25514 );

同时为了保险起见,在scan tables的过程中,DUL在log中还显示了该表的前5行记录内容,可以比对一下看看是否确实是被误删除的表。

之后,就是根据对于业务的了解,重新构建表结构了,其实对于只是删除了一张表而言,工作量还可以接受,但是如果是删除了整个USER …

5. 之后就是用sqlldr重新加载数据的事情了,不再赘述。

DUL格言是:Life without DUL

错误时刻都可能发生,为了数据的安全,为了自己工作饭碗的安全,还是做好备份吧。