2014.7.22研究恢复数据库坏块:
Oracle调用标准C的系统函数,对数据块进行读写操作,因此,坏块是有可能由以下几种原因产生:
硬件的I/O错误
操作系统的I/O错误或缓冲问题
内存或paging问题
磁盘修复工具
一个数据文件的一部分正在被覆盖
Oracle试图访问一个未被格式化的系统块失败
数据文件部分溢出
Oracle或者操作系统的bug
遇到“ORA-01578:ORACLE data block corrupted”错误
处理方法:1.rman的recover命令可以在数据库保持open状态下只恢复受损的数据块
2.如果没有备份,万不得已之下也可以采用DBMS_REPAIR包的存储过程将受损坏块隔离,同时尽可能地挽救部分数据。
rman backup命令也是检查坏数据块的好工具 一旦读取ORA-19566 即可有问题
此时可用backup validate tablespace user观察详细的信息,可查看到坏块数与跟踪文件
grep‘corrupt’/u01/app/oracle/diag/rdbms/br/br/trace/**.trc
恢复数据块:rman》recover datafile 5 block 203;
批量恢复受损的数据块:recover corruption list;
数据块坏块一号坏块,需要做:
run{
sql 'alter database datafile 5 offline';
restore datafile 5;
recover datafile 5;
sql'alter database datafile 5 online'
}
使用exp/imp恢复在这种情况下肯定会造成数据的丢失,在这种情况下应采取将数据导出然后重建表再进行导入的方法,来尽量恢复损坏数据块中的数据,但是在有坏块的情况下是不允许导出的,如下命令:Exp test/test file=t.dmp tables=t;导出命令在执行中会报ORA-01578错误,在这错误提示中会提示那个文件号的文件以及这个文件中的哪个块被损坏,如:ORA—01578:ORACLE 数据块损坏(文件号 4,块号 35)针对以上的提示首先查询那些对象被损坏:Select tablespace_name,segment_type,owner,segment_name From dba_extents Where file_id=4 and 35 between block_id and block_id+blocks-1;如果被损坏的块是索引,通常可以通过索引重建来解决,如果损坏的是数据(segment_type为table),那么通过设置如下内部事件使得Exp操作跳过坏块。Alter session set events=’10231 trace name context forever,level 10’;然后重新执行导出命令,导出相关的表,然后执行Drop Table命令删除相关表,之后重建表最后导入数据。使用DBMS_REPAIR恢复用DBMS_REPAIR当然也会丢失数据。这里不做详细的介绍,有兴趣的可以查看oracle的在线文
- 3、使用dbms_repair包进行坏块处理1)首先建立repair_table,用于存放dbms_repair.check_object检测出来的坏块信息SQL> declare2begin3dbms_repair.admin_tables4(table_name => 'REPAIR_TABLE',--表名5table_type => dbms_repair.repair_table,6action => dbms_repair.create_action,7tablespace => 'USERS');--用于指定该表存放的表空间8end;9/PL/SQL 过程已成功完成。SQL> col owner format a10SQL> col object_name format a20SQL> col object_type format a20SQL> select owner, object_name, object_type2from dba_objects3where object_name like '%REPAIR_TABLE';
OWNEROBJECT_NAMEOBJECT_TYPE---------- -------------------- --------------------SYSREPAIR_TABLETABLESYSDBA_REPAIR_TABLEVIEWOracle自动创建了一个DBA_REPAIR_TABLE视图。2)使用dbms_repair.check_object进行坏块检测SQL> set serveroutput on size 100000;SQL> declare2rpr_count int;3begin4rpr_count := 0;5dbms_repair.check_object(6schema_name => 'SYS',--指定对象模式,也就是对象的所有者7object_name => 'TEST',--指定对象名,也就是表名8repair_table_name => 'REPAIR_TABLE',9corrupt_count => rpr_count);10dbms_output.put_line('repair block count: '11||to_char(rpr_count));12end;13/repair block count: 4PL/SQL 过程已成功完成。SQL> select object_name, block_id, corrupt_type, marked_corrupt,2corrupt_description, repair_description3from repair_table;
OBJECT_NAMEBLOCK_ID CORRUPT_TYPE MARKED_COR-------------------- ---------- ------------ ----------CORRUPT_DESCRIPTION-------------------------------------------------------------------------------REPAIR_DESCRIPTION-------------------------------------------------------------------------------TEST196148 TRUEmark block software corruptTEST206148 TRUEmark block software corruptTEST236148 TRUEmark block software corruptTEST316148 TRUEmark block software corrupt通过运行dbms_repair.check_object,将坏块信息存放到了repair_table表中,其中有个字段marked_corrupt,用于标识该块是否被标识为坏块,当被标识为true时,即该块被标识为坏块。其中这一步跟oracle文档中的描述有点进入,根据oracle文档,当执行完dbms_repair.check_object时,并不会进行坏块标识,也就是marked_corrupt列的值应该为false,而只有当执行dbms_repair.fix_corrupt_blocks过程后才会进行坏块标识。3)使用dbms_repair.fix_corrupt_blocks进行坏块标识SQL> declare2fix_block_count int;3begin4fix_block_count := 0;5dbms_repair.fix_corrupt_blocks (6schema_name => 'SYS',7object_name => 'TEST',8object_type => dbms_repair.table_object,9repair_table_name => 'REPAIR_TABLE',10fix_count => fix_block_count);11dbms_output.put_line('fix blocks count: ' ||12to_char(fix_block_count));13end;14/fix blocks count: 0PL/SQL 过程已成功完成。我们可以见到到fix blocks count=0,即在上一步进行check_object时已经进行了坏块标识了,这一步其实可以省略。(不过没有测试过!)SQL> select count(*) from test;select count(*) from test*第 1 行出现错误:ORA-01578: ORACLE 数据块损坏 (文件号 7, 块号 19)ORA-01110: 数据文件 7: 'G:\ORACLE\PRODUCT\10.2.0\ORADATA\ORA10G\TEST01.DBF'此时进行查询仍然报错,因为我们只是将坏块进行了标识,当进行全表扫描的时候,仍然会查询到坏块而报错。4)使用dbms_repair.dump_orphan_keys过程来保存坏块的索引键值,然后再执行skip_corrupt_blocks过程之后,我们才能重建索引,不然重建索引时新的索引仍然会引用坏块。首先要建立ORPHAN_KEY_TABLE,此表就是用来存放坏块的索引键值。SQL> declare2begin3dbms_repair.admin_tables4(table_name => 'ORPHAN_KEY_TABLE',5table_type => dbms_repair.orphan_table,6action => dbms_repair.create_action,7tablespace => 'USERS');8end;9/
PL/SQL 过程已成功完成。然后执行过程dbms_repair.dump_orphan_keys将坏块键值存放到上面所创建的表中:SQL> declare2orph_count int;3begin4orph_count:= 0;5dbms_repair.dump_orphan_keys (6schema_name => 'SYS',7object_name => 'ID_INX',--索引的名字8object_type => dbms_repair.index_object,9repair_table_name => 'REPAIR_TABLE',--从这个表中获得坏块的信息10orphan_table_name => 'ORPHAN_KEY_TABLE',11key_count => orph_count);12dbms_output.put_line('orphan-index entries: ' || to_char(orph_count));13end;14/orphan-index entries: 491
PL/SQL 过程已成功完成。
SQL> declare2orph_count int;3begin4orph_count:= 0;5dbms_repair.dump_orphan_keys (6schema_name => 'SYS',7object_name => 'NAME_INX',8object_type => dbms_repair.index_object,9repair_table_name => 'REPAIR_TABLE',10orphan_table_name => 'ORPHAN_KEY_TABLE',11key_count => orph_count);12dbms_output.put_line('orphan-index entries: ' || to_char(orph_count));13end;14/orphan-index entries: 491PL/SQL 过程已成功完成。对每个索引都要进行dump_orphan_keys。SQL> select index_name, count(*) from orphan_key_table2group by index_name;
INDEX_NAMECOUNT(*)------------------------------ ----------ID_INX491NAME_INX4915)使用skip_corrupt_blocks,使查询或者DML时跳过坏块SQL> declare2begin3dbms_repair.skip_corrupt_blocks (4schema_name => 'SYS',5object_name => 'TEST',6object_type => dbms_repair.table_object,7flags => dbms_repair.skip_flag);8end;9/
PL/SQL 过程已成功完成。
SQL> select table_name, skip_corrupt from dba_tables2where table_name = 'TEST';
TABLE_NAMESKIP_COR------------------------------ --------TESTENABLED6)使用dbms_repair.rebuild_freelists重建freelists,使得该块不再被放到freelists,当中,也就是该块将不会再被使用。SQL> declare2begin3dbms_repair.rebuild_freelists (4schema_name => 'SYS',5object_name => 'TEST',6object_type => dbms_repair.table_object);7end;8/declare*第 1 行出现错误:ORA-10614: Operation not allowed on this segmentORA-06512: 在 "SYS.DBMS_REPAIR", line 400ORA-06512: 在 line 3不过我们可以看到,对于SYS用户下面的对象好像不能进行此操作。4、重建索引SQL> select count(id) from test;COUNT(ID)----------19998SQL> select count(name) from test;COUNT(NAME)-----------19998SQL> select count(*) from test;COUNT(*)----------19507我们可以看到上面的三个查询,对于第1和第2个使用索引进行查询和不使用索引进行查询的结果是不一样的。下面我们使用rebuild试试。SQL> alter index id_inx rebuild;索引已更改。SQL> alter index name_inx rebuild;索引已更改。SQL> select count(id) from test;COUNT(ID)----------19998SQL> select count(name) from test;COUNT(NAME)-----------19998SQL> select count(*) from test;COUNT(*)----------19507可以是不能通过rebuild来重建索引的。只能通过DROP然后再CREATE。SQL> drop index id_inx;索引已删除。SQL> drop index name_inx;索引已删除。SQL> create index id_inx on test(id);索引已创建。SQL> create index name_inx on test(name);索引已创建。SQL> select count(id) from test;COUNT(ID)----------19507SQL> select count(name) from test;
COUNT(NAME)-----------19507SQL> select count(*) from test;COUNT(*)----------19507到此该表已经可以正常使用了,但同时也丢失了一些数据,所以在使用dbms_repair进行恢复的时候要充分考虑到数据的重要性和恢复的后果。同时也应该考虑是否有其它别的恢复方法,不然贸贸然的行事最后可能得不偿失。