坏块的几种修复方式

创建测试表:
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)

上一篇 Oracle物化视图的数据来源于查询表
下一篇 Oracle 被评为 2024 年 Gartner® 分析和商业智能平台魔力象限领导者