3、使用dbms_repair包进行坏块处理
1)首先建立repair_table,用于存放dbms_repair.check_object检测出来的坏块信息
SQL> declare
2begin
3dbms_repair.admin_tables
4(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 a10
SQL> col object_name format a20
SQL> col object_type format a20
SQL> select owner, object_name, object_type
2from dba_objects
3where object_name like '%REPAIR_TABLE'; OWNEROBJECT_NAMEOBJECT_TYPE
---------- -------------------- --------------------
SYSREPAIR_TABLETABLE
SYSDBA_REPAIR_TABLEVIEW
Oracle自动创建了一个DBA_REPAIR_TABLE视图。
2)使用dbms_repair.check_object进行坏块检测
SQL> set serveroutput on size 100000;
SQL> declare
2rpr_count int;
3begin
4rpr_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: 4
PL/SQL 过程已成功完成。
SQL> select object_name, block_id, corrupt_type, marked_corrupt,
2corrupt_description, repair_description
3from repair_table;
OBJECT_NAMEBLOCK_ID CORRUPT_TYPE MARKED_COR
-------------------- ---------- ------------ ----------
CORRUPT_DESCRIPTION
-------------------------------------------------------------------------------
REPAIR_DESCRIPTION
-------------------------------------------------------------------------------
TEST196148 TRUE
mark block software corrupt
TEST206148 TRUE
mark block software corrupt
TEST236148 TRUE
mark block software corrupt
TEST316148 TRUE
mark 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> declare
2fix_block_count int;
3begin
4fix_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: 0
PL/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> declare
2begin
3dbms_repair.admin_tables
4(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> declare
2orph_count int;
3begin
4orph_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> declare
2orph_count int;
3begin
4orph_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: 491
PL/SQL 过程已成功完成。
对每个索引都要进行dump_orphan_keys。
SQL> select index_name, count(*) from orphan_key_table
2group by index_name;
INDEX_NAMECOUNT(*)
------------------------------ ----------
ID_INX491
NAME_INX491
5)使用skip_corrupt_blocks,使查询或者DML时跳过坏块
SQL> declare
2begin
3dbms_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_tables
2where table_name = 'TEST';
TABLE_NAMESKIP_COR
------------------------------ --------
TESTENABLED
6)使用dbms_repair.rebuild_freelists重建freelists,使得该块不再被放到freelists,当中,也就是该块将不会再被使用。
SQL> declare
2begin
3dbms_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 segment
ORA-06512: 在 "SYS.DBMS_REPAIR", line 400
ORA-06512: 在 line 3
不过我们可以看到,对于SYS用户下面的对象好像不能进行此操作。
4、重建索引
SQL> select count(id) from test;
COUNT(ID)
----------
19998
SQL> select count(name) from test;
COUNT(NAME)
-----------
19998
SQL> 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)
----------
19998
SQL> select count(name) from test;
COUNT(NAME)
-----------
19998
SQL> 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)
----------
19507
SQL> select count(name) from test;
COUNT(NAME)
-----------
19507
SQL> select count(*) from test;
COUNT(*)
----------
19507
到此该表已经可以正常使用了,但同时也丢失了一些数据,所以在使用dbms_repair进行恢复的时候要充分考虑到数据的重要性和恢复的后果。同时也应该考虑是否有其它别的恢复方法,不然贸贸然的行事最后可能得不偿失。