创建测试表:
SQL> drop table testtest;
Table dropped.
SQL> create table testtest as select * from dba_objects;
Table created.
SQL> create index i_test on testtest(object_id);
Index created.
SQL> col SEGMENT_NAME format a15
SQL>
SQL>
SQL> select segment_name , header_file , header_block,blocks from dba_segments where segment_name =’TESTTEST’;
SEGMENT_NAME HEADER_FILE HEADER_BLOCK BLOCKS
————— ———– ———— ———-
TESTTEST 201 1938 1536
select * from dba_extents where segment_name=’TESTTEST’ and owner=’TT’;
制造测试数据(坏块)
dd if=/dev/zero of=/u02/app/oracle/oradata/TEST/datafile/o1_mf_tt_m7kvq4fy_.dbf bs=8192 conv=notrunc seek=2180 count=1
dd if=/dev/zero of=/u02/app/oracle/oradata/TEST/datafile/o1_mf_tt_m7kvq4fy_.dbf bs=8192 conv=notrunc seek=2310 count=1
dd if=/dev/zero of=/u02/app/oracle/oradata/TEST/datafile/o1_mf_tt_m7kvq4fy_.dbf bs=8192 conv=notrunc seek=2320 count=1
检查坏块:
[oracle@test ~]$ dbv file=/u02/app/oracle/oradata/TEST/datafile/o1_mf_tt_m7kvq4fy_.dbf
DBVERIFY: Release 19.0.0.0.0 – Production on Mon Jun 24 21:08:14 2024
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
DBVERIFY – Verification starting : FILE = /u02/app/oracle/oradata/TEST/datafile/o1_mf_tt_m7kvq4fy_.dbf
Page 2180 is marked corrupt
Corrupt block relative dba: 0x32400884 (file 201, block 2180)
Completely zero block found during dbv:
Page 2310 is marked corrupt
Corrupt block relative dba: 0x32400906 (file 201, block 2310)
Completely zero block found during dbv:
Page 2320 is marked corrupt
Corrupt block relative dba: 0x32400910 (file 201, block 2320)
Completely zero block found during dbv:
DBVERIFY – Verification complete
Total Pages Examined : 2560
Total Pages Processed (Data) : 1873
Total Pages Failing (Data) : 0
Total Pages Processed (Index): 326
Total Pages Failing (Index): 0
Total Pages Processed (Other): 190
Total Pages Processed (Seg) : 0
Total Pages Failing (Seg) : 0
Total Pages Empty : 168
Total Pages Marked Corrupt : 3
Total Pages Influx : 0
Total Pages Encrypted : 0
Highest block SCN : 3492779 (0.3492779)
方法1: 简单的 row id的方式
1) 先取得坏块中ROW ID的最小值,执行以下的语句:
通过 dba_objects 和 dba_data_files 获取
SELECT dbms_rowid.rowid_create(1,,,,0) from DUAL;
SELECT dbms_rowid.rowid_create(1,75003,201,2180,0) from DUAL; AAAST7ADJAAAAiEAAA
SELECT dbms_rowid.rowid_create(1,75003,201,2310,0) from DUAL; AAAST7ADJAAAAkGAAA
SELECT dbms_rowid.rowid_create(1,75003,201,2320,0) from DUAL; AAAST7ADJAAAAkQAAA
2)取得坏块中的ROW ID的最大值,执行以下的语句:
SELECT dbms_rowid.rowid_create(1,,,+1,0) from DUAL;
SELECT dbms_rowid.rowid_create(1,75003,201,2181,0) from DUAL; AAAST7ADJAAAAiFAAA
SELECT dbms_rowid.rowid_create(1,75003,201,2311,0) from DUAL; AAAST7ADJAAAAkHAAA
SELECT dbms_rowid.rowid_create(1,75003,201,2321,0) from DUAL; AAAST7ADJAAAAkRAAA
3)建议一个临时表存储那些没有坏块的数据,执行以下的语句:
CREATE TABLE test AS SELECT * FROM testtest Where 1=2;
4)保存那些不存在坏块的数据到临时表中,执行以下的语句:
INSERT INTO test SELECT /*+ ROWID(A) */ * FROM testtest A WHERE rowid < 'AAAST7ADJAAAAiEAAA';
INSERT INTO test SELECT /*+ ROWID(A) */ * FROM testtest A WHERE rowid >= ‘AAAST7ADJAAAAiFAAA’ and rowid= ‘AAAST7ADJAAAAkHAAA’ and rowid= ‘AAAST7ADJAAAAkRAAA’;
5) 根据临时表中的数据重建表,重建表上的索引,限制。
方法2:
通过设置10231诊断事件可以在导出的时候让Oracle忽略表损坏的块,10231是Oracle的内部诊断事件,设置在全表扫描时跳过坏块的数据块,
只导出包含正确块的数据,之后把表删除,再把导出的表数据导入新表,从而修复该表。
SQL> ALTER SESSION SET EVENTS ‘10231 TRACE NAME CONTEXT FOREVER, LEVEL 10’;
Session altered.
SQL> SQL> CREATE TABLE tt AS SELECT * FROM testtest;
Table created.
SQL> select count(*) from tt;
COUNT(*)
———-
73130
方法3: 设置系统event事件 然后导出(表比较大)
ALTER SYSTEM SET EVENTS=’10231 trace name context forever,level 10′;
exp tt/tt file=/home/oracle/testtest.dmp tables=tt.testtest;
[oracle@test ~]$ exp tt/tt file=/home/oracle/testtest.dmp tables=tt.testtest;
Export: Release 19.0.0.0.0 – Production on Mon Jun 24 21:29:47 2024
Version 19.22.0.0.0
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 – Production
Version 19.22.0.0.0
Export done in US7ASCII character set and AL16UTF16 NCHAR character set
server uses AL32UTF8 character set (possible charset conversion)
About to export specified tables via Conventional Path …
. . exporting table TESTTEST 73130 rows exported
EXP-00091: Exporting questionable statistics.
Export terminated successfully with warnings.
删除表然后再导入。
完事后关闭
alter system set events=’10231 trace name context off’;
方法4: 坏块较多,可以尝试如下过程(硬解析会比较高):
set serveroutput on
DECLARE
TYPE extent_rec IS record (data_object_id number,relative_fno number,BLOCK_ID number,blocks number);
t_extent extent_rec;
bad_rows number := 0 ;
error_code number;
v_block_id number;
v_rowid rowid;
v_start_rowid rowid;
v_end_rowid rowid;
v_error_rowid rowid;
v_sql1 varchar(1000):=”;
v_sql2 varchar(1000):=”;
—坏块所在的用户表名
v_table VARCHAR(30):=’TESTTEST’;
v_owner VARCHAR(30):=’TT’;
—获取段里面每个extent里面的block_id以及block数量,extent_id=0包含段头ID,要从段头ID之后开始
CURSOR c_extent IS select b.data_object_id,a.relative_fno,c.header_block+1 start_block_id,a.blocks-(c.header_block-a.block_id+1)
from dba_extents a,dba_objects b,dba_segments c
where a.segment_name=b.object_name and a.segment_name=c.segment_name and a.owner=c.owner
and a.owner=b.owner
and b.object_name=v_table
and b.owner=v_owner
and a.extent_id=0
union all
select b.data_object_id,a.relative_fno,a.block_id start_block_id,blocks
from dba_extents a,dba_objects b
where a.segment_name=b.object_name
and a.owner=b.owner
and b.object_name=v_table
and b.owner=v_owner
and a.extent_id>0;
BEGIN
OPEN c_extent;
LOOP
FETCH c_extent INTO t_extent ;
exit when c_extent%notfound;
begin
—构造extent的最小开始rowid以及可能的最大rowid,660行是一个块里面最多存放的行数量
v_start_rowid:=dbms_rowid.rowid_create(1,t_extent.data_object_id,t_extent.relative_fno,t_extent.block_id,0);
v_end_rowid:=dbms_rowid.rowid_create(1,t_extent.data_object_id,t_extent.relative_fno,t_extent.block_id+t_extent.blocks-1,660);
—按extent进行rowid数据扫描,将正常的数据插入test_tab_normal
v_sql1:=’insert into test_tab_normal select /*+ ROWID(A) */ * from ‘||v_owner||’.’||v_table||’ A where rowid between ”’||v_start_rowid||”’ and ”’||v_end_rowid||””;
execute immediate v_sql1;
commit;
—如果按extent遇到报错或者查询数据失败则捕获异常转成按extent进行逐行的rowid扫描
exception
when others then
—按rowid逐行进行检索,插入 test_tab_normal
for a in 0..t_extent.blocks-1 loop
v_block_id:=t_extent.block_id+a;
for b in 0..660 loop
begin
v_rowid:=dbms_rowid.rowid_create(1,t_extent.data_object_id,t_extent.relative_fno,v_block_id,b);
v_sql2:=’insert into test_tab_normal select /*+ ROWID(A) */ * from ‘||v_owner||’.’||v_table||’ A where rowid =”’||v_rowid||””;
execute immediate v_sql2;
—遇到坏块报错,插入bad_rows,由于是构造的rowid,可能会话插入很多的空行数据
exception
when others then
error_code:=sqlcode;
if error_code in (-1410, -8103, -1578) then
v_error_rowid:= v_rowid;
bad_rows := bad_rows + 1;
insert into bad_rows values(v_error_rowid, error_code||’ block_id:’||v_block_id);
commit;
else
raise;
end if;
end;
end loop;
commit;
end loop;
commit;
END;
END LOOP;
commit;
CLOSE c_extent;
dbms_output.put_line(‘Total Bad Rows: ‘||bad_rows);
END;
/
SQL> select count(*) from tt.test_tab_normal;
COUNT(*)
———-
73130
SQL> select count(*) from tt.bad_rows;
COUNT(*)
———-
4627
方法5: 通过rman方式进行恢复
少量坏块,可以直接恢复指定的坏块。
blockrecover datafile 201 block 2180;
大量坏块:
backup validate datafile 201;
blockrecover corruption list;
https://blog.csdn.net/sinat_36757755/article/details/128985053
Extracting Data from a Corrupt Table using ROWID Range Scans (Doc ID 61685.1)
Handling Oracle Block Corruptions (Doc ID 28814.1)



