Oracle 在LOB段中的ORA-1578 ORA-26040 – 解决错误的脚本

如果自己搞不定可以找诗檀软件专业ORACLE数据库修复团队成员帮您恢复!

诗檀软件专业数据库修复团队

服务热线 : 13764045638 QQ号:47079569 邮箱:service@parnassusdata.com

 

 

适用于:

Oracle Database – Enterprise Edition – 版本8.1.7.0 12.1.0.1 [Release 8.1.7 to 12.1]
Oracle Database – Standard Edition – 版本8.1.7.0 12.1.0.1 [Release 8.1.7 to 12.1]
本文信息适用于任何平台。
*** 10-Feb-2011检查相关性***
*** 3-Jul-2015检查相关性***

症状

目的:

本文的目的是提供更新lob列的脚本,其使用空的lob引用一个由于NOLOGGING操作标记为corruptedlob块。

lob列被类似SELECTsql语句访问,这会防止错误ORA-1578 / ORA-26040 ,且如果需要的话可以生成表导出。

问题:

在表中读取lob列时,生成ORA-1578 ORA-26040

ORA-1578 : ORACLE data block corrupted (file # %s, block # %s)
ORA-26040: Data block was loaded using the NOLOGGING option

对生成错误的数据文件dbverify失败并生成错误DBV-200 rdbms 版本 < 10.2.0.4)或DBV-201 rdbms 版本>= 10.2.0.4):

DBV-00200: Block, dba <dba number>, already marked corrupted
DBV-00201: Block, DBA <rdba>, marked corrupt for invalid redo application

例如:

dbv file=/oracle/oradata/data.dbf blocksize=8192

DBV-00200: Block, dba 54528484, already marked corrupted
…..

可以使用dba获取相关的文件号和块号:

相关文件号:

SQL> select dbms_utility.data_block_address_file(54528484) from dual;

DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(54528484)
———————————————-
13

块号:

SQL> select dbms_utility.data_block_address_block(54528484) from dual;

DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(54528484)
———————————————–
2532

重要事项

ORA-26040 不与ORA-1578一起生成,则块由于其他原因损坏且可以使用Block Media 恢复来修复类似RMAN BLOCKRECOVER的损坏。

原因

LOB 段被定义为NOLOGGING ,且在数据文件恢复后LOB 块被Oracle标记为corrupted

解决方案

识别引用lob段的表 

当使用sql语句访问lob列时,错误示例:

ORA-01578 : ORACLE data block corrupted (file #13 block # 2532)
ORA-01110 : datafile 10: ‘/oracle/oradata/data.dbf’
ORA-26040 : Data block was loaded using the NOLOGGING option.

1. 查询 dba_extents来找出lob段名。

从以上的错误ORA-1110获取数据文件号,它表示绝对文件号 (AFN) ,并运行下一个查询来识别受影响的Lob段:

select owner, segment_name, segment_type
from   dba_extents
where  file_id = 10
and    2532 between block_id and block_id + blocks – 1;

在示例中,它返回:

owner=SCOTT
segment_name=SYS_LOB0000029815C00006$$
segment_type=LOBSEGMENT

2. 查询 dba_lobs 以识别table_name lob列名:

select table_name, column_name
from   dba_lobs
where  segment_name = ‘SYS_LOB0000029815C00006$$’
and    owner = ‘SCOTT’;

在示例中,它返回:

table_name  = EMP
column_name = EMPLOYEE_ID_LOB

XMLTYPE

这是lob段可能与XMLTYPE 相关的情况:

select table_name
from dba_lobs
where segment_name = ‘SYS_LOB0000013274C00003$$’
and owner = ‘SCOTT’;

TABLE_NAME
——————————
TABLE_WITH_XML_COLUMN

SQL> describe scott.TABLE_WITH_XML_COLUMN

Name Null?      Type
————— ————
FILENAME        VARCHAR2(64)
XML_DOCUMENT    XMLTYPE

在这里XML_DOCUMENT lob 列。

修复

3. 通过运行以下plsql脚本,识别表rowid的引用损坏lob段块:

drop table bad_rows;
create table bad_rows (row_id ROWID
,oracle_error_code number);

set concat off
set serveroutput on

declare
n number;
error_code number;
bad_rows number := 0;
ora1578 EXCEPTION;
ora600 EXCEPTION;
PRAGMA EXCEPTION_INIT(ora1578, -1578);
PRAGMA EXCEPTION_INIT(ora600, -600);

begin
for cursor_lob in (select rowid rid, &&lob_column from &&table_owner.&table_with_lob) loop
begin
n:=dbms_lob.instr(cursor_lob.&&lob_column,hextoraw(‘889911’)) ;
exception
when ora1578 then
bad_rows := bad_rows + 1;
insert into bad_rows values(cursor_lob.rid,1578);
commit;
when ora600 then
bad_rows := bad_rows + 1;
insert into bad_rows values(cursor_lob.rid,600);
commit;
when others then
error_code:=SQLCODE;
bad_rows := bad_rows + 1;
insert into bad_rows values(cursor_lob.rid,error_code);
commit;
end;
end loop;
dbms_output.put_line(‘Total Rows identified with errors in LOB column: ‘||bad_rows);
end;
/

undefine lob_column

select * from bad_rows;

当由变量值提示,以下我们的示例:

Enter value for lob_column: EMPLOYEE_ID_LOB
Enter value for table_owner: SCOTT
Enter value for table_with_lob: EMP

XMLTYPE

如果lob段与XMLTYPE相关,则使用cursor_lob.&&lob_column.getCLOBVal() (CLOB ) 以上plsql中的getBLOBVal() (BLOB)替换cursor_lob.&&lob_columnCLOB的整个列将会是:

n:=dbms_lob.instr(cursor_lob.&&lob_column.getCLOBVal(),hextoraw(‘889911’)) ;

与由变量提示时类似,在我们的例子中会是:

Enter value for lob_column: XML_DOCUMENT
Enter value for table_owner: SCOTT
Enter value for table_with_lob: TABLE_WITH_XML_COLUMN

4. 以空lob更新lob列从而防止ORA-1578 ORA-26040

SQL> set concat off
SQL> update &table_owner.&table_with_lob
set &lob_column = empty_blob() 
where rowid in (select row_id from bad_rows);

如果&lob_column 是一个CLOB NCLOB数据类型,用empty_clob替换empty_blob

XMLTYPE

如果lob 段与XMLTYPE相关,使用XMLType.createXML(”) 而不是空的lob

SQL> update scott.TABLE_WITH_XML_COLUMN
set XML_DOCUMENT = XMLType.createXML(”) 
where rowid in (select row_id from bad_rows);


5.
观察

  • 注意损坏lob块中的数据是不可拯救的,因为那里的信息是不可读的。块当前以NOLOGGING 格式损坏。
  • 设置损坏lob empty lob 会将之前映射到该lob的块添加到freelist。最终当 PCTVERSION RETENTION criteria导致空间被拯救并重新用于新数据,在相同LOB开中会再次看到错误 ORA-1578/ORA-26040Empty lob 表示在该列中被引用的损坏lobpointer被清理。损坏块本身未被访问/修复;它仅在log段的freelist元数据中被标记为free 。如果lob段仍使用更多空间继续增长,损坏块可以被尝试再使用(因为块是free)且对于请求更多空间的lob段的INSERTUPDATE语句会再次生成损坏错误。在此情况下应用以上过程后,lob 段能被移动到新的段:

alter table &table_owner.&table_with_lob move LOB (&&lob_column) store as (tablespace &tablespace_name);

  • 如果执行了alter table MOVE ,检查UNUSABLE表索引为REBUILD,因为警告日志可能有下一个信息:

Some indexes or index [sub]partitions of table <name> have been marked unusable

  • Dbverify仍会生成错误DBV-200 / DBV-201 ,直到被标记为corrupted的块的extent被另一个段再使用。
  • 在以上的plsql代码中,被发送到dbms_lob.instr中的过程hextoraw的值889911 是验证lob内容的fake值。dbms_lob.instr 应该不能找到该字符串,所以变量 “n” 应该始终返回0

参考

NOTE:290161.1 – The Gains and Pains of Nologging Operations in a Data Guard Environment
NOTE:794505.1 – ORA-1578 / ORA-26040 Corrupt blocks by NOLOGGING – Error explanation and solution

Comment

*

沪ICP备14014813号-2

沪公网安备 31010802001379号