Email: service@parnassusdata.com 7 x 24 online support!
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操作标记为corrupted的lob块。
当lob列被类似SELECT的sql语句访问,这会防止错误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_column;CLOB的整个列将会是:
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-26040。Empty lob 表示在该列中被引用的损坏lob的pointer被清理。损坏块本身未被访问/修复;它仅在log段的freelist元数据中被标记为free 。如果lob段仍使用更多空间继续增长,损坏块可以被尝试再使用(因为块是free)且对于请求更多空间的lob段的INSERT或UPDATE语句会再次生成损坏错误。在此情况下应用以上过程后,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