【Oracle数据恢复】ORA-00600[3020]错误解析

ORA-00600[3020]也被称为STUCK RECOVERY, 一般的原因是当一个数据块在被recovery恢复过程中,发现要APPLY到该块上的redo重做日志验证这个块的内容时,与ORACLE的算法不匹配,即认证redo与data block之间不一致,此时就需要报错,否则ORACLE不能在糊涂账上继续写糊涂账。

 

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

 

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

 

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

ORA-00600[3020]这个报错的相关argument在9.2中的含义是:

Arg [a] Block DBA
Arg [b] Redo Thread
Arg [c] Redo RBA Seq
Arg [d] Redo RBA Block No
Arg [e] Redo RBA Offset.

 

在ORACLE 10.1中的含义是:

Arg [a] Absolute file number of the datafile.
Arg [b] Block number
Arg [c] Block DBA

 

这个报错的模块属于内核并行内存恢复, 其具体影响是可能导致 实例在前滚时报错从而导致打开数据库OPEN Database失败。

 

解决方案: 使用recover命令时多个可能都会导致该错误,最常见的可能是数据文件没有被正常restore 到磁盘,或者restore是不完全的。 因此,首先保证整个备份被有效restore出来了,这个restore 一定要在recover database之前完成。

 

如果确认restore是完整的,但是问题仍存在,则考虑再次从backup restore然后做一个基于时间点POINT-IN-TIME的恢复,这个时间点应当早于ORA-600[3020]错误所指向的时间点。

举例来说如下面的命令:

SQL> recover database until time ‘YYYY-MON-DD:HH:MI:SS’;

当然这个错误也可能由于丢失更新lost update而造成。

在常规操作过程中, 块的更新和写是在包括一系列的数据文件、重做日志文件和归档日志文件中的。这些文件中任意一个的 丢失写都可能是ORA-00600[3020]的原因。因此也建议全面检查发生问题的操作系统和磁盘硬件。

如果是丢失写的情况,那么可以尝试从更老的备份中restore,并尝试恢复和前滚。

必要的诊断信息基本都包含在alert.log和一些trace中了,例如负责实施前滚的进程的trace和SMON的trace。

 

 

 

ORA-600 [3020]相关的一些bug列表:

 

NB Bug Fixed Description
9847338 Session hang after applying the patch for Bug 9587912 which causes ORA-600 [3020]
+ 13467683 11.2.0.2.BP15, 11.2.0.3.3, 11.2.0.3.BP04, 12.1.0.0 Join of temp and permanent tables in RAC might cause corruption of permanent table. Regression by bug 10352368
12831782 11.2.0.2.BP11, 11.2.0.3.BP01, 12.1.0.0 ORA-600 [3020] / ORA-333 Recovery of datafile or async transport do not read mirror if there is a stale block
12582839 11.2.0.3, 12.1.0.0 ORA-8103/ORA-600 [3020] on RMAN recovered locally managed tablespace
11689702 11.2.0.2.5, 11.2.0.2.BP13, 11.2.0.2.GIPSU05, 11.2.0.3, 12.1.0.0 ORA-600 [3020] during recovery after datafile RESIZE (to smaller size)
10329146 11.2.0.1.BP10, 11.2.0.2.2, 11.2.0.2.BP03, 11.2.0.2.GIBUNDLE02, 11.2.0.2.GIPSU02, 11.2.0.3, 12.1.0.0 Lost write in ASM with multiple DBWs and a disk is offlined and then onlined
10218814 11.2.0.2.2, 11.2.0.2.BP02, 11.2.0.3, 12.1.0.0 ORA-600 [3020] during recovery / on standby
+ 10209232 11.1.0.7.7, 11.2.0.1.BP08, 11.2.0.2.1, 11.2.0.2.BP02, 11.2.0.2.GIBUNDLE01, 11.2.0.3, 12.1.0.0 ORA-1578 / ORA-600 [3020] Corruption. Misplaced Blocks and Lost Write in ASM
* 10205230 11.2.0.1.6, 11.2.0.1.BP09, 11.2.0.2.2, 11.2.0.2.BP04, 11.2.0.3, 12.1.0.0 ORA-600 / corruption possible during shutdown in RAC
10094823 11.2.0.2.4, 11.2.0.2.BP09, 11.2.0.3, 12.1.0.0 Block change tracking on physical standby can cause data loss
10071193 11.2.0.2.BP02, 11.2.0.3, 12.1.0.0 Lost write / ORA-600 [kclchkblk_3] / ORA-600 [3020] in RAC – superceded
9587912 11.2.0.2, 12.1.0.0 ORA-600 [3020] in datafile that went offline/online in a RAC instance
8774868 11.2.0.1.2, 11.2.0.1.BP06, 11.2.0.2, 12.1.0.0 OERI[3020] reinstating primary
+ 8769473 11.2.0.2, 12.1.0.0 ORA-600 [kcbzib_5] on multi block read in RAC. Invalid lock in RAC. ORA-600 [3020] in Recovery
P 8635179 10.2.0.5, 11.2.0.2, 12.1.0.0 Solaris: directio may be disabled for RAC file access. Corruption / Lost Write
+ 8597106 11.2.0.1.BP06, 11.2.0.2, 12.1.0.0 Lost Write in ASM when normal redundancy is used
P 12330911 12.1 EXADATA LSI firmware for lost writes
+ 10425010 11.2.0.3, 12.1 Stale data blocks may be returned by Exadata FlashCache
8826708 10.2.0.5, 11.2.0.2 ORA-600 [3020] for block type 0x3a (58) during recovery for block restored by RMAN backup
11684626 11.2.0.1 ORA-600 [3020] on standby involving “BRR” redo when db_lost_write_protect is enabled
8230457 10.2.0.4.1, 10.2.0.5, 11.1.0.7.1, 11.2.0.1 Physical standby media recovery gets OERI[krr_media_12]
+ 7680907 10.2.0.5, 11.1.0.7.1, 11.2.0.1 ORA-600 [kclexpandlock_2] in LMS / instance crash. Incorrect locks in RAC. ORA-600 [3020] in recovery
4637668 10.2.0.3, 11.1.0.6 IMU transactions can produce out-of-order redo (OERI [3020] on recovery)
4594917 9.2.0.8, 10.2.0.2, 11.1.0.6 Write IO error can cause incorrect file header checkpoint information
4453449 10.2.0.2, 11.1.0.6 OERI:3020 / corruption errors from multiple FLASHBACK DATABASE
7197445 10.2.0.4.1, 10.2.0.5 Standby Recovery session cancelled due to ORA-600 [3020] “CHANGE IN FUTURE OF BLOCK”
5610267 10.2.0.5 MRP terminated by ORA-600[krr_media_12] / OERI:3020 after flashback
3762714 9.2.0.7, 10.1.0.4, 10.2.0.1 ALTER DATABASE RECOVER MANAGED STANDBY fails with OERI[3020]
3560209 10.2.0.1 OERI[3020] stuck recovery under RAC
3397181 9.2.0.5, 10.1.0.3, 10.2.0.1 ALTER SYSTEM KILL SESSION of recovery slave causes stuck recovery
* 3381950 10.2.0.1 Backups from RAC DB before Data Guard Failover cannot be used
3535712 9.2.0.6, 10.1.0.4 OERI[3020] / ORA-10567 from RAC with standby in max performance mode
4594912 9.2.0.8, 10.1.0.2 Incorrect checkpoint possible in datafile headers
3635331 9.2.0.6, 10.1.0.4 Stuck recovery (OERI:3020) / ORA-1172 on startup after a crash
2322620 9.2.0.1 OERI:3020 possible on recovery of LOB DATA
P+ 656370 7.3.3.4, 7.3.4.0, 8.0.3.0 AlphaNT only: Corrupt Redo (zeroed byte) OERI:3020

 

 

【Oracle数据恢复】解决ORA-00600 [KDSGRP1]错误

如果发生ORA-00600 [KDSGRP1]错误,例如:

 

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

 

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

 

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

 

ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [kdsgrp1], [], [], [], [], [], [], []
Current SQL statement for this session:
create table SYSMAN.MGMT_METRICS_RAW_COPY as select * from SYSMAN.MGMT_METRICS_RAW

$cold_kdsgrp()+1456  call     kgeasnmierr()        60000000000318D0 ?
                                                   9FFFFFFFBF3A11F8 ?
                                                   9FFFFFFFBF3A1208 ?
                                                   6000000000032D00 ?
                                                   C000000040E13740 ?
                                                   60000000000CAB08 ?
                                                   00001BD3A ?
kdsgnp()+832         call     $cold_kdsgrp()       60000000000C74B0 ?
                                                   60000000000C6D38 ?
                                                   0003FFFFF ?
                                                   C000000219E58720 ?
kafger()+4288        call     kdsgnp()             9FFFFFFFBF336E60 ?
                                                   000C07EED ?
                                                   9FFFFFFFBF336E7C ?
                                                   60000000000BA348 ?
                                                   C000000000004F25 ?
                                                   4000000002ED0F70 ?
                                                   0000182ED ?
qerixGetNonKeyCol()  call     kafger()             000000000 ?
+7344                                              9FFFFFFFFFFF22E0 ?
                                                   C0000002168BA718 ?
                                                   000000001 ? 000000001 ?
                                                   000000000 ? 000000000 ?
                                                   9FFFFFFFBF3DD228 ?
qerixFetchFastFullS  call     qerixGetNonKeyCol()  C000000219E58720 ?
can()+23536                                        9FFFFFFFBF336518 ?

 

 

 

首先确认是否HIT了Bug 8771916 – OERI [kdsgrp1] during CR read,该BUG的主要触发机制在于当一个查询涉及到对索引的唯一键查找时;但是请注意这个BUG也不是该ORA-00600 [KDSGRP1]的唯一原因。

 

保险起见的建议是

应用补丁8771916,同时考虑关闭ROW CR特性_row_cr=FALSE,虽然关闭该特性可能有极少量的性能损失。 可以通过2个指标”RowCR hits”/”RowCR attempts” 来判断是否该特性已关闭

【Oracle数据恢复】ORA-08102错误分析:JOB$与索引I_JOB_NEXT数据不一致

如果你在ORACLE数据库系统的alert.log 中反复看到类似于如下的错误:

ORA-12012 error on auto execute of job 1
ORA-08102 index key not found, obj# 239, file 1, block 1674 (2)

[oracle@vrh8 ~]$ oerr ora 8102
08102, 00000, “index key not found, obj# %s, file %s, block %s (%s)”
// *Cause:  Internal error: possible inconsistency in index
// *Action:  Send trace file to your customer support representative, along
//           with information on reproducing the error

 

则可能你已经遇到了与本例类似的问题,可以通过下面的命令来分析是否是JOB$数据字典基础表与其索引I_JOB_NEXT上的数据不一致引起的:

 

select owner , object_name , object_type , status from dba_objects
where object_id=239 ;
analyze table job$ valid structure cascade;

 

如果analyze命令报错则说明确实有不一致, 一般这种情况可以rebuild I_JOB_NEXT索引来解决, 顺序是drop index I_JOB_NEXT然后重建。

 

如果重建索引后在此analyze 仍报错,则说明 数据的不一致主要出现在表块上,对于这种情况可以采取如下的手段。

  1. 重建job$表,并将数据再次导入
  2. 重建job$上的2个索引

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

 

ASKMACLEAN专业数据库修复团队成员:

 

刘相兵  服务热线 : +86 13764045638 ;  邮箱  maclean.liu@parnassusdata.com ; QQ 47079569

胡章扬    邮箱 zyhu2006@gmail.com ;  QQ 363692655

 

【Oracle数据恢复】Redo Log重做日志文件坏块Corruption的解决 ORA-16038 ORA-00354 ORA-00353 ORA-00367 ORA-01624

当数据库打开时若某一个redo log重做日志文件存在日志损坏/坏块corruption,则可能出现以下错误:

 

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

 

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

 

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

 

ORA-16038 log %s sequence# %s cannot be archived
ORA-354 corrupt redo log block header
ORA-353 log corruption near block  change time 
ORA-367 checksum error in log file header

[oracle@mlab2 ~]$ oerr ora 16038
16038, 00000, "log %s sequence# %s cannot be archived"
// *Cause:  An attempt was made to archive the named file, but the
//          file could not be archived. Examine the secondary error
//          messages to determine the cause of the error.
// *Action: No action is required.

[oracle@mlab2 ~]$ oerr ora 354
00354, 00000, "corrupt redo log block header"
// *Cause:  The block header on the redo block indicated by the accompanying
//          error, is not reasonable.
// *Action: Do recovery with a good version of the log or do time based
//          recovery up to the indicated time. If this happens when archiving,
//          archiving of the problem log can be skipped by clearing the log
//          with the UNARCHIVED option. This must be followed by a backup of
//          every datafile to insure recoverability of the database.
[oracle@mlab2 ~]$ oerr ora 353
00353, 00000, "log corruption near block %s change %s time %s"
// *Cause:  Some type of redo log corruption has been discovered. This error
//          describes the location of the corruption. Accompanying errors
//          describe the type of corruption.
// *Action: Do recovery with a good version of the log or do incomplete
//          recovery up to the indicated change or time.
www.askmac.cn
[oracle@mlab2 ~]$ oerr ora 367
00367, 00000, "checksum error in log file header"
// *Cause:  The file header for the redo log contains a checksum that does
//          not match the value calculated from the file header as read from
//          disk. This means the file header is corrupted
// *Action: Find the correct file and try again.

 

 

 

 

对于不同状态的在线日志文件online redo logfile 损坏/坏块,有不同的解决方案。

对于以下2种情况将不能drop online的redo logfile,他们是:

  1. 如果仅仅有2个redo logfile groups
  2. 损坏的redo logfile文件属于当前日志组 current logfile group; V$LOG.STATUS=CURRENT

 

 

解决方案大致如下:

 

可以通过clear 命令来清理有问题的日志文件,语法如下:

alter database clear <unarchived> logfile group <integer>;
alter database clear <unarchived> logfile ‘<filename>’;

 

例如:

alter database clear logfile group 1;
alter database clear unarchived logfile group 1;

 

 

对于status=current 或者 status=active的日志将无法被清理, 如果清理会报 ORA-01624:

 

SQL> alter database clear unarchived logfile group 5;
alter database clear unarchived logfile group 5
*
ERROR at line 1:
ORA-01624: log 5 needed for crash recovery of instance G10R25 (thread 1)
ORA-00312: online log 5 thread 1: '/s01/G10R25/onlinelog/o1_mf_5_954q1vdo_.log'
www.askmac.cn
[oracle@vrh8 ~]$ oerr ora 1624
01624, 00000, "log %s needed for crash recovery of instance %s (thread %s)"
// *Cause:  A log cannot be dropped or cleared until the thread's checkpoint
//          has advanced out of the log.
// *Action: If the database is not open, then open it. Crash recovery will
//          advance the checkpoint. If the database is open force a global
//          checkpoint. If the log is corrupted so that the database cannot
//          be opened, it may be necessary to do incomplete recovery until
//          cancel at this log.

 

 

对于status=active的日志较为简单,只要能顺利完成一个alter system checkpoint就可以将其状态改为INACTIVE之后再CLEAR。

对于status=current的online redo logfile则比较麻烦,一般需要动用隐藏参数”_ALLOW_RESETLOGS_CORRUPTION”

ORA-00600 [16703], [1403], [20]一例

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

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

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

 

 

某用户核心数据库出现ORA-00600 [16703], [1403], [20] 错误导致数据库无法OPEN,其详细报错如下:

 

 

SQL*Plus: Release 11.2.0.1.0 Production on Thu May 2 21:05:48 2013

Copyright (c) 1982, 2009, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> alter database open;
alter database open
*
ERROR at line 1:
ORA-01092: ORACLE instance terminated. Disconnection forced
ORA-00704: bootstrap process failure
ORA-00704: bootstrap process failure
ORA-00600: internal error code, arguments: [16703], [1403], [20], [], [], [],
[], []
Message 3144 not found;  product=RDBMS; facility=ORA
Message 3142 not found;  product=RDBMS; facility=ORA

 

 

该ORA-00600 [16703]内部错误常见于 当数据库核心字典对象 例如OBJ$ TAB$等核心数据字典表出现讹误时触发; 对于该类该ORA-00600 [16703]错误 可以首先了解其trace文件:

 

SMON: enabling cache recovery
Errors in file /s01/diag/rdbms/test10/TEST10/trace/TEST10_ora_4317.trc  (incident=8553):
ORA-00600: internal error code, arguments: [16703], [1403], [20], [], [], [], [], []
Incident details in: /s01/diag/rdbms/test10/TEST10/incident/incdir_8553/TEST10_ora_4317_i8553.trc
Errors in file /s01/diag/rdbms/test10/TEST10/trace/TEST10_ora_4317.trc:
ORA-00704: bootstrap process failure
ORA-00704: bootstrap process failure
ORA-00600: internal error code, arguments: [16703], [1403], [20], [], [], [], [], []
Errors in file /s01/diag/rdbms/test10/TEST10/trace/TEST10_ora_4317.trc:
ORA-00704: bootstrap process failure
ORA-00704: bootstrap process failure
ORA-00600: internal error code, arguments: [16703], [1403], [20], [], [], [], [], []
Error 704 happened during db open, shutting down database
USER (ospid: 4317): terminating the instance due to error 704
Instance terminated by USER, pid = 4317
ORA-1092 signalled during: alter database open...
opiodr aborting process unknown ospid (4317) as a result of ORA-1092
Thu May 02 21:05:52 2013
ORA-1092 : opitsk aborting process


/s01/diag/rdbms/test10/TEST10/incident/incdir_8553/TEST10_ora_4317_i8553.trc


*** 2013-05-02 21:05:52.076
dbkedDefDump(): Starting incident default dumps (flags=0x2, level=3, mask=0x0)
----- Current SQL Statement for this session (sql_id=a01hp0psv0rrh) -----
alter database open

----- Call Stack Trace -----


gesiv()+110         call     kgeriv()             00A99D540 ? 7F6AF2FBF5B0 ?
                                                   000000000 ? 000000000 ?
                                                   000000000 ? 000000000 ?
ksesic2()+194        call     kgesiv()             00A99D540 ? 7F6AF2FBF5B0 ?
                                                   00000413F ? 000000002 ?
                                                   7FFF0E034070 ? 000000000 ?
kqlbpob()+1383       call     ksesic2()            00A99D540 ? 000000000 ?
                                                   00000057B ? 000000000 ?
                                                   000000014 ? 000008000 ?
kqrlfc()+678         call     kqlbpob()            000000033 ? 000000000 ?
                                                   00000057B ? 000000000 ?
                                                   000000014 ? 000008000 ?
kqlbplc()+175        call     kqrlfc()             000000033 ? 7FFF0E034480 ?
                                                   00000057B ? 000000000 ?
                                                   000000014 ? 000008000 ?
kqlblfc()+275        call     kqlbplc()            000000000 ? 7FFF0E034480 ?
                                                   00000057B ? 000000000 ?
                                                   000000014 ? 000008000 ?
adbdrv()+40310       call     kqlblfc()            000000000 ? 7FFF0E0399C0 ?
                                                   00000057B ? 000000000 ?
                                                   000000014 ? 000008000 ?
opiexe()+15552       call     adbdrv()             000000000 ? 0A2B94B98 ?


*****************************************************
----- Session Open Cursors -----

----------------------------------------
Cursor#2(0x7f6af2a719a8) state=BOUND curiob=0x7f6af2a815b0
 curflg=f fl2=200000 par=0x7f6af2a71918 ses=0xaa848988
----- Dump Cursor sql_id=bqbdby3c400p7 xsc=0x7f6af2a815b0 cur=0x7f6af2a719a8 -----

LibraryHandle:  Address=a98ba1c0 Hash=d84002a7 LockMode=N PinMode=0 LoadLockMode=0 Status=VALD
  ObjectName:  Name=select rowcnt,blkcnt,empcnt,avgspc,chncnt,avgrln,nvl(degree,1), nvl(instances,1) from tab$ where obj# = :1

 

 

若看到—– Session Open Cursors —–中有 select rowcnt,blkcnt,empcnt,avgspc,chncnt,avgrln,nvl(degree,1), nvl(instances,1) from tab$ where obj# = :1 此语句则一般说明是TAB$ 基表受损, 需要修复该表; 但如果是该表大面积损坏则没有修复的可能。

 

可以考虑使用PRM-DUL类工具抽取该损坏数据库中的数据并重建数据库。

 

【转】如何格式化不属于任何段的损坏块

【转】如何格式化不属于任何段的损坏块

d1. Rman 备份失败,显示 ORA-19566 错误,且被报告的坏块不属于任何对象
2. Dbverify 显示存在坏块
3. 坏块不属于任何对象
CAUSE
在重新使用和重新格式化坏块之前,RMAN 和 DBV 仍会一直报告坏块。
SOLUTION
下面提供一种可以解决该问题的方式。请注意,我们并不保证这一方式适用,但它曾被多次用于解决该问题。此外,如果某一特定数据文件中坏块数量很多,请在第 6 步中出现块编号提示时,输
入该数据文件中坏块的最高块编号。
如果坏块位于数据文件的可用空间中,Oracle 将自动对该块进行重新格式化并重新使用。
在本文档中,我们尝试手动重新格式化坏块。
第 1 步 - 确定损坏的数据文件
查看 ORA-19566 消息,确定损坏的数据文件。
示例:
RMAN-03009: failure of backup command on nm4501 channel at 04/29/2005 09:44:41
ORA-19566: exceeded limit of 0 corrupt blocks for file E:\xxxx\test.ORA.
坏块位于文件 E:\xxxx\test.ORA 中。
第 2 步 - 在受影响的数据文件上运行 DBV/Rman 验证并检查坏块
在报告坏块的数据文件上运行 dbverify。
示例输出:
DBVERIFY: Release 9.2.0.3.0 - Production on Thu Aug 25 11:15:54 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
DBVERIFY - Verification starting : FILE = E:\xxxx\test.ORA
Page 48740 is marked corrupt ***
Corrupt block relative dba: 0x01c0be64 (file 7, block 48740)
Bad check value found during dbv:
Data in bad block -
type: 0 format: 2 rdba: 0x0000be64
last change scn: 0x0000.00000000 seq: 0x1 flg: 0x05
consistency value in tail: 0x00000001
check value in block header: 0xb964, computed block checksum: 0x2a5a
spare1: 0x0, spare2: 0x0, spare3: 0x0


DBVERIFY - Verification complete
Total Pages Examined : 64000
Total Pages Processed (Data) : 0
Total Pages Failing (Data) : 0
Total Pages Processed (Index): 1751
Total Pages Failing (Index): 0
Total Pages Processed (Other): 45
Total Pages Processed (Seg) : 0
Total Pages Failing (Seg) : 0
Total Pages Empty : 62203
Total Pages Marked Corrupt : 1
请注意,在数据文件 7 中,报告 块 48740 损坏。
或者
对于整个数据库
Rman> backup validate check logical database ;
For specific datafile
Rman> backup validate check logical datafile <fileno> ;
Once done query
SQL>Select * from v$database_block_corruption ;
**如果在第 2 步中 v$database_block_corruption 报告的块数量很多,接下来最好使用第 4 步而不是第 3 步。
第 3 步 - 检查块是否属于任何对象
查询 dba_extents,确认坏块是否属于任何对象。
SQL> select segment_name, segment_type, owner
from dba_extents
where file_id = <Absolute file number>
and <corrupted block number> between block_id
and block_id + blocks -1;
如果块不属于任何对象,查询dba_free_space 确认坏块是否属于数据文件的可用空间。
SQL> Select * from dba_free_space where file_id= <Absolute file number>
and <corrupted block number> between block_id and block_id + blocks -1;
第 4 步 - 查找受影响的块并验证其是否属于任何段的一种比较好的方法是使用 RMAN,这种方法既好用又便捷。
如果在第 2 步中已经运行了 rman 验证,请直接转到下面给出的 sqlplus 脚本,以确认对象。
$ rman target / nocatalog
or
$ rman target sys/ nocatalog
run {
allocate channel d1 type disk;
allocate channel d2 type disk;
.....................................................................
... multiple channels may be allocated for parallelizing purposes ...
... depends: RMAN - Min ( MAXOPENFILES , FILESPERSET ) ... ... ...
... Defaults: MAXOPENFILES =8, FILESPERSET =64 ... ... ...
.....................................................................
allocate channel dn type disk;
backup check logical validate database;
release channel d1;
release channel d2;
......................................
... release all channels allocated ...
......................................
release channel dn;
}
重要说明:-
如果数据库处于 NOARCHIVELOG 模式,那么必须在数据库正常关闭之后的装载阶段运行上述 RMAN 命令。
否则将输出错误
RMAN-03009: failure of backup command on d1 channel at 12/07/2009 18:55:25
ORA-19602: cannot backup or copy active file in NOARCHIVELOG mode
此限制已在 11g 中解除。
*** 在进行进一步操作之前,*必须*运行并完成 RMAN 命令“backup check logical validate database”。

*** 在进行进一步操作之前,*必须*运行并完成 RMAN 命令“backup check logical validate database”。
*** 此命令完成(基于文件)后将会填充“v$database_block_corruption”视图。
*** 如果未完成,在接下来的步骤中您就有可能得到无效/不完整的信息。
运行以下 sql 查询,以确定块是位于可用空间中还是已占用空间中
set lines 200 pages 10000
col segment_name format a30
SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
, greatest(e.block_id, c.block#) corr_start_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
- greatest(e.block_id, c.block#) + 1 blocks_corrupted
, null description
FROM dba_extents e, v$database_block_corruption c
WHERE e.file_id = c.file#
AND e.block_id <= c.block# + c.blocks - 1
AND e.block_id + e.blocks - 1 >= c.block#
UNION
SELECT s.owner, s.segment_type, s.segment_name, s.partition_name, c.file#
, header_block corr_start_block#
, header_block corr_end_block#
, 1 blocks_corrupted
, 'Segment Header' description
FROM dba_segments s, v$database_block_corruption c
WHERE s.header_file = c.file#
AND s.header_block between c.block# and c.block# + c.blocks - 1
UNION
SELECT null owner, null segment_type, null segment_name, null partition_name, c.file#
, greatest(f.block_id, c.block#) corr_start_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
- greatest(f.block_id, c.block#) + 1 blocks_corrupted
, 'Free Block' description
FROM dba_free_space f, v$database_block_corruption c
WHERE f.file_id = c.file#
AND f.block_id <= c.block# + c.blocks - 1
AND f.block_id + f.blocks - 1 >= c.block#
ORDER BY file#, corr_start_block#;
第 5 步 - 以非 SYS 或 SYSTEM(用户)的用户身份创建一个虚拟表
SQL> connect scott/password
在包含出现坏块的数据文件的表空间中创建虚拟表,并使用 nologging 选项,以防止生成 redo记录:
SQL> create table s (
n number,
c varchar2(4000)
) nologging tablespace <tablespace name having the corrupt block> ;
可以根据具体环境的不同使用不同的存储参数。
通过查询 user_segments,确定在正确的表空间中创建了表:
SQL> select segment_name,tablespace_name from user_segments
where segment_name='S' ;
第 6 步 - 在虚拟表上创建触发器,一旦重新使用坏块,该触发器便会引发异常
以 sys 身份连接,并创建以下触发器:
请注意,在出现文件号提示时,输入相关文件号(v$datafile 中的 rfile# 值)
CREATE OR REPLACE TRIGGER corrupt_trigger
AFTER INSERT ON scott.s
REFERENCING OLD AS p_old NEW AS new_p
FOR EACH ROW
DECLARE
corrupt EXCEPTION;
BEGIN
IF (dbms_rowid.rowid_block_number(:new_p.rowid)=&blocknumber)
and (dbms_rowid.rowid_relative_fno(:new_p.rowid)=&filenumber) THEN
RAISE corrupt;
END IF;
EXCEPTION
WHEN corrupt THEN
RAISE_APPLICATION_ERROR(-20000, 'Corrupt block has been formatted');
END;
/
出现块编号提示时,输入坏块的块编号。
出现文件号提示时,输入损坏的数据文件的相关文件号(v$datafile 中的 rfile# 值)。
第 7 步 - 为受影响的数据文件中的表分配空间。
如果使用的是自动段空间管理的表空间(ASSM),因为ASSM表空间extent的分配是由自己决定的,可能需要多次运行本步骤来分配多个extents,
并且定期查看视图dba_extents确保空闲空间已经分配出去。
首先通过查询 dba_free_space 查找 extent 大小
SQL> Select BYTES from dba_free_space where file_id=<file no> and <corrupt block no> between block_id and block_id + blocks -1;
BYTES
---------------- ---------- ---------- ---------- ---------- ------------
65536
如果使用 64 K,
例如,从 E:\xxxx\test.ORA 下面分配 64 K 的空间,如下例所示:

例如,从 E:\xxxx\test.ORA 下面分配 64 K 的空间,如下例所示:
SQL> alter table scott.s
allocate extent (DATAFILE 'E:\xxxx\test.ORA' SIZE 64K);
如果使用 1M,
SQL> Select BYTES from dba_free_space where file_id=<file no> and <corrupt block no> between block_id and block_id + blocks -1;
BYTES
---------------- ---------- ---------- ---------- ---------- ------------
1048576
例如,从 E:\xxxx\test.ORA 下面分配 1M 的空间,如下例所示:
SQL> alter table scott.s
allocate extent (DATAFILE 'E:\xxxx\test.ORA' SIZE 1M);
或者
使用下面的 for 循环
BEGIN
for i in 1..1000000 loop
EXECUTE IMMEDIATE 'alter table scott.s allocate extent (DATAFILE '||'''E:\xxxx\test.ORA''' ||'SIZE 64K) ';
end loop;
end ;
/
请注意,您需要相应地更改大小(1M、64K 或 128k),并按照要求更改循环的次数。
继续分配空间直到坏块成为 scott.s 的一部分 — 使用以下查询进行检查:
SQL> select segment_name, segment_type, owner
from dba_extents
where file_id = <Absolute file number>
and <corrupt block number> between block_id
and block_id + blocks -1 ;
注意:明智的做法是确保数据文件的 AUTOEXTEND 处于关闭状态,以防止其扩展
第 8 步 - 向虚拟表中插入数据以格式化块
示例代码(取决于表空间的大小,循环的次数可能发生变化):
BEGIN
FOR i IN 1..1000000000 LOOP
INSERT /*+ APPEND */ INTO scott.s select i, lpad('REFORMAT',3092, 'R') from dual;
commit ;
END LOOP;
END;
或者
BEGIN
FOR i IN 1..1000000000 LOOP
INSERT INTO scott.s VALUES(i,'x');
END LOOP;
END;
/
或使用以下包含 2 个循环的代码:
Begin
FOR i IN 1..1000000000 loop
for j IN 1..1000 loop
Insert into scott.s VALUES(i,'x');
end loop;
commit;
END LOOP;
END;
每向表中插入一行就会触发触发器,且一旦向坏块中插入第一行数据,就会产成 ORA-20000 异常。
第 9 步 - 通过运行 DBV 和 Rman 备份确定数据文件中的坏块情况
在损坏的数据文件上运行 dbverify。此操作将不会显示坏块。
RMAN 备份不会报告此块上的任何错误。
第 10 步 - 删除第 4 步中创建的虚拟表
SQL> DROP TABLE scott.s ;
如果版本为 10gr1 及以上,同时使用purge选项以清空回收站
第 11 步 – 执行手动日志切换和检查点

第 11 步 – 执行手动日志切换和检查点
执行两次日志切换和检查点,以便将在内存中格式化的块写入到磁盘并使 dbverify 不再报告错误
SQL>Alter system switch logfile ; --> Do this couple of time
SQL>Alter system checkpoint ;


第 12 步 - 删除第 6 步中创建的触发器
SQL> DROP triggercorrupt_trigger ;
同样要注意:
Bug 7381632 - ORA-1578 Free corrupt blocks may not be reformatted when Flashback is enabled

ORA-1624与redo log损坏



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

 

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

 

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

当数据库处于打开状态下多个redo logfile中的一个损坏了,则可能出现如下的错误信息:

ORA-16038 log %s sequence# %s cannot be archived
ORA-354 corrupt redo log block header
ORA-353 log corruption near block <num> change <str >time <str>
ORA-367 checksum error in log file header
ORA-368 checksum error in redo log block

 

[oracle@mlab2 ~]$ oerr ora 16038
16038, 00000, “log %s sequence# %s cannot be archived”
// *Cause: An attempt was made to archive the named file, but the
// file could not be archived. Examine the secondary error
// messages to determine the cause of the error.
// *Action: No action is required.

 

其他场景下可以通过drop redo logfile来绕过,但是如果对应的redo logfile是被crash/instance recovery 所需要则无法drop掉。

在线的redo logfile 可能无法drop的2个原因是:

  • 仅仅只有2组redo logfile
  • 受损坏的redo log file属于当前日志组

针对上述描述的问题可以考虑通过clear logfile 的方式来解决,如:

 

 

alter database clear <unarchived> logfile group <integer>;
alter database clear <unarchived> logfile '<filename>';
alter database clear logfile group 1;
alter database clear unarchived logfile group 1;

 

对于v$LOG中status=CURRENT或者status=ACTIVE的在线redo logfile一般是clear不掉的,会报一个错误:

 

oerr ora 01624
01624, 00000, “log %s needed for crash recovery of instance %s (thread %s)”
// *Cause: A log cannot be dropped or cleared until the thread’s checkpoint
// has advanced out of the log.
// *Action: If the database is not open, then open it. Crash recovery will
// advance the checkpoint. If the database is open force a global
// checkpoint. If the log is corrupted so that the database cannot
// be opened, it may be necessary to do incomplete recovery until
// cancel at this log.

 

注意用户不应当把’alter database clear logfile’拿来经常使用,由此而引发的缺失归档日志,会导致完全恢复变得不可能。在执行clear logfile后迅速做全库的备份吧!

 

 

如何找回被create or replace覆盖的PL/SQL对象

有同学在T.askmac.cn上提问关于10gR2下原存储过程procedure,因为开发人员误操作create or replace 使用同样的过程名导致原存储过程被覆盖,希望通过Oracle技术手段在不停机的前提下找回原存储过程procedure。

 

这里Maclean 提供2种10gR2以后可以在线挽救被覆盖PL/SQL对象的方案:

 

方案1: 利用Flashback Query 闪回特性,该方案并不要求数据库已启用flashback database,唯一和最关键的要求是create or replace时递归SQL删除source$数据字典基础表相关的undo data,不要因为时间过久而被重用:

 

SQL> select * from V$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------
www.askmac.cn

SQL> create or replace procedure maclean_proc as
   2  begin
   3  execute immediate 'select 1 from dual';
   4  end;
   5  /

Procedure created.

SQL> select * from dba_source where name='MACLEAN_PROC';

 OWNER      NAME                           TYPE               LINE TEXT
 ---------- ------------------------------ ------------ ---------- --------------------------------------------------
 SYS        MACLEAN_PROC                   PROCEDURE             1 procedure maclean_proc as
 SYS        MACLEAN_PROC                   PROCEDURE             2 begin
 SYS        MACLEAN_PROC                   PROCEDURE             3 execute immediate 'select 1 from dual';
 SYS        MACLEAN_PROC                   PROCEDURE             4 end;

SQL> select current_scn from v$database;

 CURRENT_SCN
 -----------
     2660057

create or replace procedure maclean_proc as
begin
-- I am new procedure
execute immediate 'select 2 from dual';
end;
/

Procedure created.

SQL> select current_scn from v$database;

 CURRENT_SCN
 -----------
     2660113

 SQL> select * from dba_source where name='MACLEAN_PROC';

 OWNER      NAME                           TYPE               LINE TEXT
 ---------- ------------------------------ ------------ ---------- --------------------------------------------------
 SYS        MACLEAN_PROC                   PROCEDURE             1 procedure maclean_proc as
 SYS        MACLEAN_PROC                   PROCEDURE             2 begin
 SYS        MACLEAN_PROC                   PROCEDURE             3 -- I am new procedure
 SYS        MACLEAN_PROC                   PROCEDURE             4 execute immediate 'select 2 from dual';
 SYS        MACLEAN_PROC                   PROCEDURE             5 end;

SQL> create table old_source as select * from dba_source as of scn 2660057 where name='MACLEAN_PROC';

Table created.

SQL> select * from old_source where name='MACLEAN_PROC';

 OWNER      NAME                           TYPE               LINE TEXT
 ---------- ------------------------------ ------------ ---------- --------------------------------------------------
 SYS        MACLEAN_PROC                   PROCEDURE             1 procedure maclean_proc as
 SYS        MACLEAN_PROC                   PROCEDURE             2 begin
 SYS        MACLEAN_PROC                   PROCEDURE             3 execute immediate 'select 1 from dual';
 SYS        MACLEAN_PROC                   PROCEDURE             4 end;

 

 

如果无法找出准备的scn作为flashback query闪回原点,那么可以尝试使用as of timestamp多次指定不同的时间点,一般只要PL/SQL对象被覆盖的时间不要太久且实例对undo的并发事务需求较低时,都可以通过以上方法找回被replace/drop 覆盖或删除的PL/SQL对象。

 

方案2 利用logminer找出replace/drop PL/SQL对象的递归SQL主要是DELETE语句,利用logminer的UNDO SQL来找回PL/SQL对象的定义。

该方案的前提是启用了归档且相关的archivelog未被删除,数据库最好是启用了最小追加日志 minimal supplemental logging,否则可能出现挖掘出的Unsupported SQLREDO的情况:

 

 

create or replace替换 一个 procedure存储过程的递归SQL包括以下这些, 主要是删除原procedure在数据字典中的记录并插入新的记录, source$字典基表是找回存储过程的重点:

 

 

SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

Database altered.

SQL> create or replace procedure maclean_proc as
  2  begin
  3  execute immediate 'select 1 from dual';
  4  end;
  5  /

Procedure created.

SQL>
SQL> oradebug setmypid;
Statement processed.
SQL>
SQL> oradebug event 10046 trace name context forever,level 12;
Statement processed.
SQL>
SQL> create or replace procedure maclean_proc as
  2  begin
  3  execute immediate 'select 2 from dual';
  4  end;
  5  /

Procedure created.

SQL> oradebug tracefile_name
/s01/admin/G10R25/udump/g10r25_ora_4305.trc

[oracle@vrh8 ~]$ egrep  "update|insert|delete|merge"  /s01/admin/G10R25/udump/g10r25_ora_4305.trc
delete from procedureinfo$ where obj#=:1
delete from argument$ where obj#=:1
delete from procedurec$ where obj#=:1
delete from procedureplsql$ where obj#=:1
delete from procedurejava$ where obj#=:1
delete from vtable$ where obj#=:1
insert into procedureinfo$(obj#,procedure#,overload#,procedurename,properties,itypeobj#) values (:1,:2,:3,:4,:5,:6)
insert into argument$( obj#,procedure$,procedure#,overload#,position#,sequence#,level#,argument,type#,default#,in_out,length,precision#,scale,radix,charsetid,charsetform,properties,type_owner,type_name,type_subname,type_linkname,pls_type) values (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16,:17,:18,:19,:20,:21,:22,:23)
insert into procedureplsql$(obj#,procedure#,entrypoint#) values (:1,:2,:3)
update procedure$ set audit$=:2,options=:3 where obj#=:1
delete from source$ where obj#=:1
insert into source$(obj#,line,source) values (:1,:2,:3)
delete from idl_ub1$ where obj#=:1 and part=:2 and version<>:3
delete from idl_char$ where obj#=:1 and part=:2 and version<>:3
delete from idl_ub2$ where obj#=:1 and part=:2 and version<>:3
delete from idl_sb4$ where obj#=:1 and part=:2 and version<>:3
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
update idl_sb4$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
update idl_ub1$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
update idl_char$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
update idl_ub2$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
delete from idl_ub1$ where obj#=:1 and part=:2 and version<>:3
delete from idl_char$ where obj#=:1 and part=:2 and version<>:3
delete from idl_ub2$ where obj#=:1 and part=:2 and version<>:3
delete from idl_sb4$ where obj#=:1 and part=:2 and version<>:3
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
delete from idl_ub1$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from idl_char$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from idl_ub2$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from idl_sb4$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from idl_ub1$ where obj#=:1 and part=:2 and version<>:3
delete from idl_char$ where obj#=:1 and part=:2 and version<>:3
delete from idl_ub2$ where obj#=:1 and part=:2 and version<>:3
delete from idl_sb4$ where obj#=:1 and part=:2 and version<>:3
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
update idl_sb4$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
update idl_ub1$ set piece#=:1 ,length=:2 , piece=:3 where obj#=:4 and part=:5 and piece#=:6 and version=:7
delete from idl_char$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from idl_ub2$ where obj#=:1 and part=:2 and (piece#<:3 or piece#>:4) and version=:5
delete from error$ where obj#=:1
delete from settings$ where obj# = :1
insert into settings$(obj#, param, value) values (:1, :2, :3)
delete from warning_settings$ where obj# = :1
insert into warning_settings$(obj#, warning_num, global_mod, property) values (:1, :2, :3, :4)
delete from dependency$ where d_obj#=:1
delete from access$ where d_obj#=:1
insert into dependency$(d_obj#,d_timestamp,order#,p_obj#,p_timestamp, property, d_attrs)values (:1,:2,:3,:4,:5,:6, :7)
insert into access$(d_obj#,order#,columns,types) values (:1,:2,:3,:4)
update obj$ set obj#=:6,type#=:7,ctime=:8,mtime=:9,stime=:10,status=:11,dataobj#=:13,flags=:14,oid$=:15,spare1=:16, spare2=:17 where owner#=:1 and name=:2 and namespace=:3 and(remoteowner=:4 or remoteowner is null and :4 is null)and(linkname=:5 or linkname is null and :5 is null)and(subname=:12 or subname is null and :12 is null)

 

而drop procedure也会类似的去source$删除该PL/SQL对应的记录:

 

SQL>  oradebug setmypid;
Statement processed.
SQL>  oradebug event 10046 trace name context forever,level 12;
Statement processed.
SQL> drop procedure maclean_proc;

Procedure dropped.

SQL> oradebug tracefile_name
/s01/admin/G10R25/udump/g10r25_ora_4331.trc

delete from context$ where obj#=:1
delete from dir$ where obj#=:1
delete from type_misc$ where obj#=:1
delete from library$ where obj#=:1
delete from procedure$ where obj#=:1
delete from javaobj$ where obj#=:1
delete from operator$ where obj#=:1
delete from opbinding$ where obj#=:1
delete from opancillary$ where obj#=:1
delete from oparg$ where obj# = :1
delete from com$ where obj#=:1
delete from source$ where obj#=:1
delete from idl_ub1$ where obj#=:1 and part=:2
delete from idl_char$ where obj#=:1 and part=:2
delete from idl_ub2$ where obj#=:1 and part=:2
delete from idl_sb4$ where obj#=:1 and part=:2
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
delete from idl_ub1$ where obj#=:1 and part=:2
delete from idl_char$ where obj#=:1 and part=:2
delete from idl_ub2$ where obj#=:1 and part=:2
delete from idl_sb4$ where obj#=:1 and part=:2
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
delete from idl_ub1$ where obj#=:1 and part=:2
delete from idl_char$ where obj#=:1 and part=:2
delete from idl_ub2$ where obj#=:1 and part=:2
delete from idl_sb4$ where obj#=:1 and part=:2
delete from ncomp_dll$ where obj#=:1 returning dllname into :2
delete from error$ where obj#=:1
delete from settings$ where obj# = :1
delete from procedureinfo$ where obj#=:1
delete from argument$ where obj#=:1
delete from procedurec$ where obj#=:1
delete from procedureplsql$ where obj#=:1
delete from procedurejava$ where obj#=:1
delete from vtable$ where obj#=:1
delete from dependency$ where d_obj#=:1
delete from access$ where d_obj#=:1
delete from objauth$ where obj#=:1
update obj$ set obj#=:6,type#=:7,ctime=:8,mtime=:9,stime=:10,status=:11,dataobj#=:13,flags=:14,oid$=:15,spare1=:16, spare2=:17 where owner#=:1 and name=:2 and namespace=:3 and(remoteowner=:4 or remoteowner is null and :4 is null)and(linkname=:5 or linkname is null and :5 is null)and(subname=:12 or subname is null and :12 is null)

 

 

开始正觉挖掘日志寻找source$相关的redo:

 

SQL> alter system switch logfile;

System altered.

SQL> select sequence#,name from v$archived_log where sequence#=(select max(sequence#) from v$archived_log);

 SEQUENCE#
----------
NAME
--------------------------------------------------------------------------------
       242
/s01/flash_recovery_area/G10R25/archivelog/2012_05_21/o1_mf_1_242_7vnm13k6_.arc

SQL> exec dbms_logmnr.add_logfile ('/s01/flash_recovery_area/G10R25/archivelog/2012_05_21/o1_mf_1_242_7vnm13k6_.arc',options => dbms_logmnr.new);

PL/SQL procedure successfully completed.

SQL> exec dbms_logmnr.start_logmnr(options => dbms_logmnr.dict_from_online_catalog);

PL/SQL procedure successfully completed.

SQL> select sql_redo,sql_undo from v$logmnr_contents where seg_name = 'SOURCE$' and operation='DELETE';

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '1' and "SOURCE" = 'procedure maclean_proc as
' and ROWID = 'AAAABIAABAAALpyAAN';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','1','procedure maclean_proc as
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '2' and "SOURCE" = 'begin
' and ROWID = 'AAAABIAABAAALpyAAO';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','2','begin
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '3' and "SOURCE" = 'execute immediate ''select 1 from dual'';
' and ROWID = 'AAAABIAABAAALpyAAP';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','3','execute immediate ''select 1 from dual'';
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '4' and "SOURCE" = 'end;' and ROWID = 'AAAABIAABAAALpyAAQ';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','4','end;');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '1' and "SOURCE" = 'procedure maclean_proc as
' and ROWID = 'AAAABIAABAAALpyAAJ';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','1','procedure maclean_proc as
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '2' and "SOURCE" = 'begin
' and ROWID = 'AAAABIAABAAALpyAAK';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','2','begin
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '3' and "SOURCE" = 'execute immediate ''select 2 from dual'';
' and ROWID = 'AAAABIAABAAALpyAAL';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','3','execute immediate ''select 2 from dual'';
');

delete from "SYS"."SOURCE$" where "OBJ#" = '56059' and "LINE" = '4' and "SOURCE" = 'end;' and ROWID = 'AAAABIAABAAALpyAAM';
insert into "SYS"."SOURCE$"("OBJ#","LINE","SOURCE") values ('56059','4','end;');

 

 

 

可以利用 logminer获得的UNDO SQL替换其中的表名source$为临时表,将DELETE的数据插入到这张临时表中,之后查询SOURCE字段就可以获得被覆盖前的PL/SQL对象的DDL定义。

Archivelog Completed Before VS UNTIL TIME

有网友在T.askmac.cn上 提问关于”DELETE ARCHIVELOG ALL COMPLETED BEFORE” 与 “DELETE ARCHIVELOG UNTIL TIME “的区别。为了了解这2个命令细微的差别,我们先来温习一些 ARCHIVED LOG的知识。

 

V$ARCHIVED_LOG:

FIRST_TIME             DATE     Timestamp of the first change
NEXT_TIME              DATE     Timestamp of the next change
COMPLETION_TIME        DATE     Time when the archiving completed

 

 

FIRST_TIME代表该归档日志中LOW SCN对应的时间戳, 而NEXT_TIME代表HIGH SCN对应的时间戳; COMPLETION_TIME指该日志实际归档成功的时间,当归档可以快速完成时 NEXT_TIME往往等于COMPLETION_TIME,但是也存在因为LOGFILE SIZE尺寸较大导致archive归档操作持续较长时间,导致 NEXT_TIME << COMPLETION_TIME的情况存在。

 

 

我们来看一个例子:

 

首先通过修改log_archive_max_processes=1,保证实例只拥有一个ARC0归档进程:

 

 

SQL> select * from V$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL>
SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------
www.askmac.cn

SQL> alter system set log_archive_max_processes=1;
System altered.

[oracle@vrh8 udump]$ ps -ef|grep arc|grep -v grep
oracle   21777     1  0 07:22 ?        00:00:00 ora_arc0_G10R25

SQL> alter session set nls_date_format='YYYY-MM-DD hh24:mi:ss';
Session altered.

SQL> select  SEQUENCE#, FIRST_CHANGE# from v$log where status='CURRENT';

 SEQUENCE# FIRST_CHANGE#
---------- -------------
       232       2476748

 

 

CURRENT LOGFILE当前在线日志的SEQUENCE#=232, FIRST_CHANGE#=2476748。

我们利用oradebug suspend 命令将ARC0归档后台进程强制挂起,这将导致归档长时间无法完成; 注意不要在生产环境做这样的操作!!

 

 

SQL> oradebug setospid 21777;
Oracle pid: 20, Unix process pid: 21777, image: oracle@vrh8.oracle.com (ARC0)

SQL> oradebug suspend;
Statement processed.

SQL> alter system switch logfile;

System altered.

SQL> alter system switch logfile;

System altered.

WAIT A MOMENT;

SQL> select sequence#,name,first_time,next_time,COMPLETION_TIME from v$archived_log where sequence# =( select max(sequence#) from v$archived_log);

 SEQUENCE#
----------
NAME
--------------------------------------------------------------------------------
FIRST_TIME          COMPLETION_TIME
------------------- -------------------
       231
/s01/flash_recovery_area/G10R25/archivelog/2012_05_17/o1_mf_1_231_7v9rh2rg_.arc
2012-05-17 07:32:44 2012-05-17 07:32:50

 

 

可以看到手动suspend ARC0后switch logfile,归档没有照常发生,V$ARCHIVED_LOG中最大的SEQUENCE#仍是 231。 之后我们resume ARC0:

 

SQL> exec dbms_lock.sleep(60);

SQL> oradebug resume;
Statement processed.

SQL>  set linesize 80  pagesize 1400;
SQL>  select sequence#,name,first_time,next_time,COMPLETION_TIME from v$archived_log where sequence# =( select max(sequence#) from v$archived_log);

 SEQUENCE#
----------
NAME
--------------------------------------------------------------------------------
FIRST_TIME          NEXT_TIME           COMPLETION_TIME
------------------- ------------------- -------------------
       232
/s01/flash_recovery_area/G10R25/archivelog/2012_05_17/o1_mf_1_232_7v9s3dcf_.arc
2012-05-17 07:32:50 2012-05-17 07:41:32 2012-05-17 07:43:40

 

 

NEXT_TIME=07:41:32 而 COMPLETION_TIME= 07:43:40,相差了2分钟左右。

 

DUMP LOGFILE可以了解更多信息:

 

 

SQL> alter system dump logfile '/s01/flash_recovery_area/G10R25/archivelog/2012_05_17/o1_mf_1_232_7v9s3dcf_.arc';

System altered.

SQL> oradebug setmypid;
Statement processed.

SQL> oradebug tracefile_name
/s01/admin/G10R25/udump/g10r25_ora_21848.trc

SQL> !vi /s01/admin/G10R25/udump/g10r25_ora_21848.trc

 Low  scn: 0x0000.0025cacc (2476748) 05/17/2012 07:32:50
 Next scn: 0x0000.0025cb8f (2476943) 05/17/2012 07:41:32
 Enabled scn: 0x0000.0005eca9 (388265) 11/07/2011 03:58:11
 Thread closed scn: 0x0000.0025cacc (2476748) 05/17/2012 07:32:50

 

 

以上我们复习了 关于ARCHIVED LOG 的FIRST_TIME和 COMPLETION_TIME的知识,接下来我们实际来了解”DELETE ARCHIVELOG ALL COMPLETED BEFORE” 与 “DELETE ARCHIVELOG UNTIL TIME “的区别。

 

RMAN会通过$ORACLE_HOME/rdbms/admin/recover.bsq将RMAN命令解析成PL/SQL包的调用,包括:DBMS_RCVMAN和DBMS_BACKUP_RESTORE等内置PACKAGE。

 

当使用BACKUP/DELETE ARCHIVELOG “Completed Before”/”UNTIL TIME”时DBMS_RCVMAN会调用以下一个SQL语句,查询V$ARCHIVED_LOG视图中是否有满足条件的归档日志:

 

HASH_VALUE=  3114867949

SELECT :B20 TYPE_CON,
       RECID KEY_CON,
       RECID RECID_CON,
       STAMP STAMP_CON,
       TO_NUMBER(NULL) SETSTAMP_CON,
       TO_NUMBER(NULL) SETCOUNT_CON,
       TO_NUMBER(NULL) BSRECID_CON,
       TO_NUMBER(NULL) BSSTAMP_CON,
       TO_NUMBER(NULL) BSKEY_CON,
       TO_NUMBER(NULL) BSLEVEL_ CON,
       TO_CHAR(NULL) BSTYPE_CON,
       TO_NUMBER(NULL) ELAPSESECS_CON,
       TO_NUMBER(NULL) P IECECOUNT_CON,
       NAME FILENAME_CON,
       TO_CHAR(NULL) TAG_CON,
       TO_NUMBER(NULL) COPYNUM BER_CON,
       STATUS STATUS_CON,
       BLOCKS BLOCKS_CON,
       BLOCK_SIZE BLOCKSIZE_CON,
       'DISK' DEVICETYPE_CON,
       COMPLETION_TIME COMPTIME_CON,
       TO_DATE(NULL) CFCREATIONTIME_CON,
       TO_NUMBER(NULL) PIECENUMBER_CON,
       TO_DATE(NULL) BPCOMPTIME_CON,
       TO_CHAR(NULL) BPC OMPRESSED_CON,
       :B19 TYPE_ACT,
       TO_NUMBER(NULL) FROMSCN_ACT,
       TO_NUMBER(NULL) TOSCN _ACT,
       TO_DATE(NULL) TOTIME_ACT,
       TO_NUMBER(NULL) RLGSCN_ACT,
       TO_DATE(NULL) RLGTIM E_ACT,
       TO_NUMBER(NULL) DBINCKEY_ACT,
       TO_NUMBER(NULL) LEVEL_ACT,
       TO_NUMBER(NULL) DFNUMBER_OBJ,
       TO_NUMBER(NULL) DFCREATIONSCN_OBJ,
       TO_NUMBER(NULL) CFSEQUENCE_OBJ,
       TO_DATE(NULL) CFDATE_OBJ,
       SEQUENCE# LOGSEQUENCE_OBJ,
       THREAD# LOGTHREAD_OBJ,
       RES ETLOGS_CHANGE# LOGRLGSCN_OBJ,
       RESETLOGS_TIME LOGRLGTIME_OBJ,
       FIRST_CHANGE# LOGLO WSCN_OBJ,
       FIRST_TIME LOGLOWTIME_OBJ,
       NEXT_CHANGE# LOGNEXTSCN_OBJ,
       NEXT_TIME LOGN EXTTIME_OBJ,
       DECODE(END_OF_REDO_TYPE, 'TERMINAL', 'YES', 'NO') LOGTERMINAL_OBJ,
       T O_CHAR(NULL) CFTYPE_OBJ,
       TO_NUMBER(NULL) KEEP_OPTIONS,
       TO_DATE(NULL) KEEP_UNTIL,
       TO_NUMBER(NULL) AFZSCN_ACT,
       TO_DATE(NULL) RFZTIME_ACT,
       TO_NUMBER(NULL) RFZSCN_A CT,
       TO_CHAR(NULL) MEDIA_CON,
       IS_RECOVERY_DEST_FILE ISRDF_CON
  FROM V$ARCHIVED_LOG
 WHERE (:B18 IS NULL OR THREAD# = :B18)
   AND (:B17 IS NULL OR SEQUENCE# = :B17)
   AND (:B16 IS NULL OR FIRST_CHANGE# = :B16)
   AND (:B15 IS NULL OR NAME LIKE :B15)
   AND (:B14 IS NULL OR COMPLETION_TIME >= :B14)
   AND (:B13 IS NULL OR COMPLETION_TIME <= :B13)
   AND DECODE(:B10,
              :B12,
              DECODE(STATUS, 'A', :B9, :B11),
              DBMS _RCVMAN.ISSTATUSMATCH(STATUS, :B10)) = :B9
   AND STANDBY_DEST = 'NO'
   AND (ARCHIVE D = 'YES')
   AND (:B8 IS NULL OR THREAD# = :B8)
   AND (:B7 IS NULL OR SEQUENCE# >= :B7)
   AND (:B6 IS NULL OR SEQUENCE# <= :B6)
   AND (:B5 IS NULL OR NEXT_CHANGE# > :B5)
   AND (:B4 IS NULL OR FIRST_CHANGE# < :B4)
   AND (:B3 IS NULL OR NAME LIKE :B 3)
   AND (:B2 IS NULL OR NEXT_TIME > :B2)
   AND (:B1 IS NULL OR FIRST_TIME <= :B1)
 ORDER BY RESETLOGS_CHANGE#,
          RESETLOGS_TIME,
          THREAD#,
          SEQUENCE#,
          LOGTERMINAL_OB    J DESC,
          STAMP_CON         DESC

 

 

已知该语句的HASH_VALUE=3114867949 ,虽然该语句使用了绑定变量且似乎10046 TRACE capture不到其BIND VALUE,但仍可以通过v$sql_bind_capture视图找到:

 

 

当DELETE ARCHIVELOG UNTIL TIME ‘SYSDATE-7’;执行时:

 

col name for a20
col value_string for a50		  

SQL> select name,value_string from v$sql_bind_capture where hash_value='3114867949';

:B20
:B19
:B18                 NULL
:B18                 NULL
:B17                 NULL
:B17                 NULL
:B16                 NULL
:B16                 NULL
:B15                 NULL
:B15                 NULL
:B14                 NULL
:B14                 NULL
:B13                 NULL
:B13                 NULL
:B10                 27
:B12                 1
:B9                  1
:B11                 0
:B10                 27
:B9                  1
:B8                  NULL
:B8                  NULL
:B7                  NULL
:B7                  NULL
:B6                  NULL
:B6                  NULL
:B5                  NULL
:B5                  NULL
:B4                  NULL
:B4                  NULL
:B3                  NULL
:B3                  NULL
:B2                  NULL
:B2                  NULL
:B1                  05/10/12 07:15:26
:B1                  05/10/12 07:15:26

36 rows selected.

 

其中有意义的绑定值为:

:B1 05/10/12 07:15:26 =》即SYSDATE – 7

可以在上述SQL中找到相关条件:B1 IS NULL OR FIRST_TIME <= :B1,即 FIRST_TIME <= ‘SYSDATE-7’;

即 UNTIL TIME 的TIME指的是 ARCHIVELOG的FIRST_TIME ,即归档日志中LOW SCN对应的时间戳;其意思为找出所有LOW SCN TIMESTAMP小于等于指定的时间变量的归档日志。

 

当DELETE ARCHIVELOG ALL COMPLETED BEFORE ‘SYSDATE-7’;执行时:

 

SQL> select name,value_string from v$sql_bind_capture where hash_value='3114867949';

:B20
:B19
:B18                 NULL
:B18                 NULL
:B17                 NULL
:B17                 NULL
:B16                 NULL
:B16                 NULL
:B15                 NULL
:B15                 NULL
:B14                 NULL
:B14                 NULL
:B13                 05/10/12 07:21:00
:B13                 05/10/12 07:21:00
:B10                 27
:B12                 1
:B9                  1
:B11                 0
:B10                 27
:B9                  1
:B8                  NULL
:B8                  NULL
:B7                  NULL
:B7                  NULL
:B6                  NULL
:B6                  NULL
:B5                  0
:B5                  0
:B4                  281474976710656
:B4                  281474976710656
:B3                  NULL
:B3                  NULL
:B2                  NULL
:B2                  NULL
:B1                  NULL
:B1                  NULL

其中有意义的绑定值为 :B13 05/10/12 07:21:00=> ‘SYSDATE-7′

SQL中的相关条件:B13 IS NULL OR COMPLETION_TIME <= :B13 即   COMPLETION_TIME <=’SYSDATE=7’;

COMPLETED BEFORE指的是ARCHIVELOG的COMPLETION_TIME,即实际归档操作完成的时间;其意思为找出所有归档完成时间小于指定的时间变量的归档日志。

 

 

当DELETE ARCHIVELOG ALL COMPLETED  AFTER    ‘SYSDATE-7’;执行时:

 

SQL> select name,value_string from v$sql_bind_capture where hash_value='3114867949';

:B20
:B19
:B18                 NULL
:B18                 NULL
:B17                 NULL
:B17                 NULL
:B16                 NULL
:B16                 NULL
:B15                 NULL
:B15                 NULL
:B14                 05/10/12 07:23:03
:B14                 05/10/12 07:23:03
:B13                 NULL
:B13                 NULL
:B10                 27
:B12                 1
:B9                  1
:B11                 0
:B10                 27
:B9                  1
:B8                  NULL
:B8                  NULL
:B7                  NULL
:B7                  NULL
:B6                  NULL
:B6                  NULL
:B5                  0
:B5                  0
:B4                  281474976710656
:B4                  281474976710656
:B3                  NULL
:B3                  NULL
:B2                  NULL
:B2                  NULL
:B1                  NULL
:B1                  NULL

 

 

:B14 IS NULL OR COMPLETION_TIME >= :B14,类似的AFTER操作仅仅是从小于等于变成了大于等于。

COMPLETED AFTER指的是ARCHIVELOG的COMPLETION_TIME,即实际归档操作完成的时间;其意思为找出所有归档完成时间大于等于指定的时间变量的归档日志。

 

Summary :

UNTIL TIME的TIME  指的是 ARCHIVELOG的FIRST_TIME ,即归档日志中LOW SCN对应的时间戳;其意思为找出所有LOW SCN TIMESTAMP小于等于指定的时间变量的归档日志。

COMPLETED BEFORE  指的是ARCHIVELOG的COMPLETION_TIME,即实际归档操作完成的时间;其意思为找出所有归档完成时间小于指定的时间变量的归档日志。

COMPLETED AFTER   指的是ARCHIVELOG的COMPLETION_TIME,即实际归档操作完成的时间;其意思为找出所有归档完成时间大于等于指定的时间变量的归档日志。

 

Question:

搞清楚这些细节对实际的工作由什么意义?

 

Answer:

ARCHIVELOG相关过滤条件 UNTIL TIME 和 COMPLETED BEFORE是存在区别的,在平时备份BACKUP时可能感受不到这种区别。

试想这样一个场景,
SEQUENCE A 的ARCHIVELOG 的First TIME为 07:45 、 NEXT TIME为08:10、归档操作耗费了1分钟即COMPLETION_TIME为08:11
SEQUENCE A+1即后续的一个ARCHIVELOG的 FIRST TIME为08:10,NEXT TIME为08:30……..

我们以08:00为时间变量,

若使用DELETE  ARCHIVELOG UNTIL TIME 08:00 ,因为SENQUENCE A的FIRST_TIME <08:00,所以SEQUENCE A将被删除,若没有相应的归档备份或COPY,则意味着08:00~08:10 这段时间将变成unrecoverable;

若使用DELETE ARCHIVELOG ALL  COMPLETED  BEFORE 08:00,因为SENQUENCE A的COMPLETION_TIME>08:00,所以SEQUENCE A将不被删除。

 

来实际体验一下这个结论,SEQUENCE 232的
FIRST_TIME=2012-05-17 07:32:50,
NEXT TIME=2012-05-17 07:41:32,
COMPLETION_TIME=2012-05-17 07:43:40

 

 

RMAN> DELETE ARCHIVELOG  ALL COMPLETED BEFORE "to_timestamp('2012-05-17 07:40:00','YYYY-MM-DD hh24:mi:ss')";

released channel: ORA_DISK_1
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=140 devtype=DISK

RMAN> DELETE ARCHIVELOG  UNTIL TIME "to_timestamp('2012-05-17 07:40:00','YYYY-MM-DD hh24:mi:ss')";

released channel: ORA_DISK_1
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=140 devtype=DISK

List of Archived Log Copies
Key     Thrd Seq     S Low Time  Name
------- ---- ------- - --------- ----
39      1    232     A 17-MAY-12 /s01/flash_recovery_area/G10R25/archivelog/2012_05_17/o1_mf_1_232_7v9s3dcf_.arc

Do you really want to delete the above objects (enter YES or NO)? y
deleted archive log
archive log filename=/s01/flash_recovery_area/G10R25/archivelog/2012_05_17/o1_mf_1_232_7v9s3dcf_.arc recid=39 stamp=783503020
Deleted 1 objects

【转】处理 Oracle 7/8/8i/9i/10g/11g 中的数据块损坏/讹误/坏块

 

 

适用于:

Oracle Database – Enterprise Edition – 版本 7.0.16.0 到 11.2.0.2.0 [发行版 7.0 到 11.2]
本文档所含信息适用于所有平台
用途

本文章讨论如何处理 Oracle 数据文件中的一个或多个坏块,并介绍了处理这些坏块的主要方法。在采取任何措施之前,请先阅读完整篇文章。

详细信息

文档历史记录

 

本文中所提到的所有 SQL 语句均适用于 SQL*Plus(8.1 或更高版本),或作为 SYSDBA 用户连接时,适用于 Server Manager (Oracle7/8.0)。(例如:“connect / as sysdba”或“connect internal”)

 

 

简介

 

本文章讨论如何处理 Oracle 数据文件中的一个或多个坏块,并介绍了处理这些坏块的主要方法。在采取任何措施之前,请先阅读完整篇文章。

本文档未介绍内存坏块问题(通常为 ORA-600 [17xxx] 类型错误)。

 

注意:如果在启动时出现 ORA-1578 问题,请与当地支持中心联系,以获得参考建议。

Doc 106638.1 – 本文档不会提供给客户,但其中的相关步骤可以由经验丰富的支持分析人员提供。

 

 

本文章介绍了许多类型的错误,很多其他地方也可能引用到本文章。重要的是,您需要知道关于每个坏块的以下信息:

 

包含坏块的文件的绝对文件编号 (FILE NUMBER)。
本文中称为“&AFN”。

包含坏块的文件的名称。
本文中称为“&FILENAME”。

(如果您知道文件编号,但不知道文件名,则可以使用 V$DATAFILE 来获取文件名:

SELECT name FROM v$datafile WHERE file#=&AFN;

如果文件号未显示在 Oracle8i 的 V$DATAFILE 中,且 &AFN 大于 DB_FILES 参数值,则该文件可能是临时文件。在这种情况下,可以使用以下查询找到文件名:

SELECT name FROM v$tempfile

WHERE file#=(&AFN – &DB_FILES_value);

)

文件中坏块的块编号。
本文中称为“&BL”。

包含受影响块的表空间编号和名称。 本文中称为“&TSN”(表空间编号)和“&TABLESPACE_NAME”。

 

如果您不知道这些信息,请使用以下查询找到它们:

 

SELECT ts# “TSN” FROM v$datafile WHERE file#=&AFN;

 

SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN;

表空间中的坏块大小。
本文中称为“&TS_BLOCK_SIZE”。

 

对于 Oracle 9i+,请运行以下查询来确定相应的块大小:

 

 

SELECT block_size FROM dba_tablespaces

WHERE tablespace_name =

(SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN);

 

对于 Oracle 7、8.0 和 8.1:

数据库中所有表空间使用相同的块大小。对于这些版本,使用命令”SHOW PARAMETER DB_BLOCK_SIZE” 返回的值作为 “ &TS_BLOCK_SIZE”

 

例如:对于 ORA-1578 错误:

ORA-01578: ORACLE data block corrupted (file # 7, block # 12698)

ORA-01110: data file 22: ‘/oracle1/oradata/V816/oradata/V816/users01.dbf’

那么:

&AFN 为 “22” (从错误ORA-1110部分获得)

&RFN 为 “7” (从错误ORA-1578的”file #”部分获得)

&BL 为 “12698” (从错误ORA-1578的”block #”部分获得)

&FILENAME 为 ‘/oracle1/oradata/V816/oradata/V816/users01.dbf’

&TSN 及其他信息可以从上面提到的SQL语句获得

 

对于其他错误(ORA-600、ORA-1498 等),上述值应由 Oracle Support 提供给您,或从涵盖相关错误的文章中获取。

 

 

 

 

对于某些错误,如 ORA-1410“invalid ROWID(无效 ROWID)”、ORA-12899“value too large for column(列值过大)”等,未给出损坏的文件/块的详细信息。对于此类情况,Document 869305.1 可以帮助您定位损坏的行。

 

 

概述处理坏块的步骤

 

有多种原因可能导致坏块,包括:

 

– 坏的 IO 硬件/固件

– OS 问题

– Oracle 问题

– 对于执行过“UNRECOVERABLE”或“NOLOGGING”操作的数据库进行恢复(在这种情况下可能产生 ORA-1578 错误 – 如下方所示)

 

产生 Oracle 错误的时间点可能要比最初发生任何块损坏的时间点晚得多。

 

在遇到坏块时,我们通常无从了解根本原因,并且在大多数情况下,当下最迫切的需求是重新启动数据库并使其运行起来,正因如此,本文将介绍用于解决坏块问题的步骤,如下所列:

 

1) 确定坏块问题的范围,并确定这些问题是持久性问题还是暂时性问题。

 

如果问题涉及范围很大,或错误不稳定,则关键在于先识别原因(检查硬件等)。这点很重要,因为如果是底层硬件出现错误,恢复系统便毫无意义。

 

(2) 更换或拆下任何有问题的或可疑的硬件。

 

(3) 确定哪些数据库对象受到影响。

 

4) 选择最合适的数据库恢复/数据抢救选项。

 

 

对于上述所有步骤,最好应收集证据并详细记录所采取的措施。本文中的“证据>>”标签列出了应收集的信息,以帮助您识别问题的根本原因。

 

 

由于 NOLOGGING 或 UNRECOVERABLE 操作导致的坏块

 

如 果对某个对象执行了 NOLOGGING(或 UNRECOVERABLE)操作,随后又恢复了包含该对象的数据文件,则受到 NOLOGGING 操作影响的数据块将被标记为“坏块”,当您访问该数据块时将显示 ORA-1578 错误。在 Oracle8i 中,还将显示 ORA-26040 错误“ORA-26040: Data block was loaded using the NOLOGGING option(数据块使用 NOLOGGING 选项加载)”,此时原因一目了然,而较早版本中则没有附加这条错误消息。如果坏块是由于对执行过 NOLOGGING 操作的数据文件进行恢复而产生的,则可以使用本文中从第 3 节“受影响的对象有哪些?”开始之后介绍的内容,但请注意以下问题:

(a) 恢复操作无法找回受 NOLOGGING 操作影响的数据

 

(b) 块内的数据无法抢救

 

 

(1) 确定坏块问题的范围

 

每次发生坏块错误时,都应记下完整的错误消息,并查看该实例的告警日志和跟踪文件,以了解任何相关的错误。首先进行这些步骤非常重要,这可以评估该损坏是单个块,还是由于 UNRECOVERABLE 操作产生的错误,抑或是更严重的问题。

 

使用 DBVERIFY 扫描受影响的文件(以及一切重要的文件)也是不错的办法,这样可以检查是否有其他坏块,从而确定问题的范围。有关使用 DBVERIFY 的详细信息,请参阅 Document 35512.1

 

一旦确定了损坏的文件/块组合列表,即可使用以下步骤来帮助确定应采取何种措施。

 

证据>>

– 完整记录初始错误,以及发生错误的应用程序的详细信息。

 

– 及时地保存从告警日志中首次 (FIRST) 记录到问题前数小时到当前时间点所提取的内容。

– 保存告警日志中提到的任何跟踪文件。

– 记录最近遇到的任何 OS 问题。

– 记录是否正在使用任何特殊功能,例如:ASYNC IO、快速写入磁盘选项等。

– 记录当前的备份位置(日期、类型等)

– 记录数据库是否处于 ARCHIVELOG 模式,例如:在SQL*Plus(或 Server Manager)中运行“ARCHIVE LOG LIST”

 

 

(2) 更换或拆下可疑硬件

 

大多数坏块问题是由故障硬件导致的。如果出现硬件错误或可疑组件,最好进行修复,或者在执行恢复操作之前,确保在单独的磁盘子系统上有足够的可用空间用于恢复。

 

您可以使用以下步骤移动数据文件:

 

1. 确保要迁移的文件已离线或数据库实例处于 MOUNT 状态(未打开)

 

2. 将该数据文件物理还原(或复制)到新位置

例如:/newlocation/myfile.dbf

 

3. 将该文件的新位置告知 Oracle。

例如:ALTER DATABASE RENAME FILE ‘/oldlocation/myfile.dbf’

TO ‘/newlocation/myfile.dbf’;

(请注意,您不能对临时文件进行重命名,而应删除临时文件并在新位置重新创建)

 

4. 使相关数据文件/表空间上线(如果数据库已打开)

 

 

重要信息:如果存在多个错误(不是由于 NOLOGGING操作导致的)

或 受影响文件所在的 OS 层面出现错误

或 错误是暂时性的且游离不定,

那么,如果不解决底层问题或准备另外的磁盘空间,那么进行任何操作都是毫无意义的。

与硬件供应商联系,以全面检查系统,并联系 Oracle Support,告知所有错误详情。

请注意:如果硬件检查失败,即表明存在硬件问题,但硬件检查成功通过却并不能证明没有硬件相关问题 — 硬件测试报告成功但确实存在底层错误,这种情况也是经常发生。

 

 

如果使用了任何特殊 IO 选项,例如 direct IO、async IO 或类似的选项,最好将其禁用,以消除这些选项成为潜在问题原因的可能性。

 

 

 

(3) 受影响的对象有哪些?

 

在决定如何恢复之前,最好先确定哪些对象受到了影响,因为坏块可能发生在那些容易被重新创建的对象中。例如:对于只有 5 行数据的表中发生的坏块,删除并重新创建表可能要比执行恢复快得多。

 

对于每个坏块,请收集下表中的信息。进行此操作的步骤如下所述。

 

针对每次坏块需记录的信息

初始错误

绝对文件号
&AFN

相关文件号
&RFN

块编号

&BL

表空间

段类型


所有者.名称

相关对象

恢复选项

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

下列说明将有助于您针对每个坏块填写此表。

 

“初始错误”

最初报告的错误。例如:ORA-1578/ORA-1110、ORA-600 和所有参数等。

 

“绝对文件号”、“相关文件号”和“块号”

文件号和块号应该已在错误中显示,或由 Oracle Support 提供,或在指引您参考本文章的其他文章的步骤中提供。

 

在 Oracle8/8i/9i/10g 中:绝对文件号和相关文件号通常是一样的,但也可能不同(尤其是在数据库是由 Oracle7 迁移而来的情况下)。请务必获得正确的 &AFN 和 &RFN 编号,否则您可能最终抢救的是错误的对象!!

 

ORA-1578 报告相关文件号,绝对文件号在伴随的 ORA-1110 错误中显示。对于 ORA-600 错误,您应该会被告知绝对文件号。

 

下列查询将显示数据库中数据文件的绝对和相关文件号:

 

SELECT tablespace_name, file_id “AFN”, relative_fno “RFN”

FROM dba_data_files;

 

在 Oracle8i/9i/10g 中:

除了上述关于 Oracle8 的说明外,从 Oracle8i 开始将拥有临时文件。下列查询将显示数据库中临时文件的绝对和相关文件号:

 

SELECT tablespace_name, file_id+value “AFN”, relative_fno “RFN”

FROM dba_temp_files, v$parameter

WHERE name=’db_files’;

 

在 Oracle7 中:“绝对文件号”和“相关文件号”使用相同的文件号

 

 

 

“段类型”、“所有者”、“名称”和“表空间”

在给定坏块的绝对文件号“&AFN”和块编号“&B”的情况下,下列查询将显示对象的段类型、所有者和名称,数据库必须打开才能使用此查询:

 

SELECT tablespace_name, segment_type, owner, segment_name

FROM dba_extents

WHERE file_id = &AFN

and &BL between block_id AND block_id + blocks – 1

;

 

如果坏块位于临时文件中,则上述查询将不会返回任何数据。

对于临时文件,“段类型”应为“TEMPORARY”。

 

如 果上述查询未返回行,也可能是因为坏块是本地管理表空间 (Locally Managed Tablespace, LMT) 中的段头。当坏块为 LMT 中的段头块时,上述查询将在 alert.log 中生成一个坏块消息,但查询不会失败。在这种情况下,请使用以下查询:

 

SELECT owner, segment_name, segment_type, partition_name

FROM dba_segments

WHERE header_file = &AFN

and header_block = &BL

;

 

参考 Note 819533.1

 

 

按段类型分类的“相关对象”和可能的“恢复选项”:

相关对象和能够使用的恢复选项取决于 SEGMENT_TYPE。对于各种最常见的段类型,其他查询和可能的恢复选项如下所列。

 

CACHE

– 如果段类型为 CACHE,请再次检查您是否输入了正确的 SQL语句 和参数。

如果仍得到相同结果,请联系 Oracle Support 并上传您所了解的全部信息。

 

选项:

可能需要恢复数据库。

 

 

CLUSTER

– 如果段类型为 CLUSTER,则应确定它包含哪些表。

例如:

SELECT owner, table_name

FROM dba_tables

WHERE owner=’&OWNER’

AND cluster_name=’&SEGMENT_NAME’

;

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非数据字典cluster,可能的选项包括:

恢复

或 抢救cluster中所有表的数据

然后重新创建cluster及其所有表

 

cluster可能包含多个表,因此在做出决策之前,最好先收集cluster中每个表的信息。

 

 

INDEX PARTITION

– 如果段类型为 INDEX PARTITION,请记录名称和所有者,然后确定哪些分区受到影响:

 

SELECT partition_name

FROM dba_extents

WHERE file_id = &AFN

AND &BL BETWEEN block_id AND block_id + blocks – 1

;

 

然后按照处理 INDEX 段的步骤继续下面的操作。

 

选项:

使用下列语句可以重建索引分区:

ALTER INDEX xxx REBUILD PARTITION ppp;

(请注意下方“重建索引”中所述的 REBUILD 选项)

 

 

INDEX

– 如果段类型为 INDEX,而且所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

 

对于非字典 INDEX 或 INDEX PARTITION,确定索引位于哪个表中:

例如:

SELECT table_owner, table_name

FROM dba_indexes

WHERE owner=’&OWNER’

AND index_name=’&SEGMENT_NAME’

;

 

并确定索引是否支持约束:

 

Eg: SELECT owner, constraint_name, constraint_type, table_name

FROM dba_constraints

WHERE owner=’&TABLE_OWNER’

AND constraint_name=’&INDEX_NAME’

;

 

CONSTRAINT_TYPE 的可能值包括:

 

P 索引支持主键约束。

U 索引支持唯一约束。

 

 

如果索引支持主键约束(类型“P”),则确认主键是否被任何外键约束引用:

例如:

SELECT owner, constraint_name, constraint_type, table_name

FROM dba_constraints

WHERE r_owner=’&TABLE_OWNER’

AND r_constraint_name=’&INDEX_NAME’

;

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典索引,可能的选项包括:

恢复

或 重建索引(任何相关联的约束会随之禁用/启用)

(请注意下方“重建索引”中所述的 REBUILD 选项)

 

 

ROLLBACK

– 如果段类型为 ROLLBACK,请联系 Oracle Support,因为 ROLLBACK 段坏块需要特殊处理。

选项:

可能需要恢复数据库。

 

 

TYPE2 UNDO

– TYPE2 UNDO 是系统管理的 undo 段,它是 rollback 段的一种特殊形式。这些段的坏块需要特殊处理。

 

选项:

可能需要恢复数据库。

 

 

TABLE PARTITION

– 如果段类型为 TABLE PARTITION,请记录名称和所有者,然后确定哪些分区受到影响:

 

SELECT partition_name

FROM dba_extents

WHERE file_id = &AFN

AND &BL BETWEEN block_id AND block_id + blocks – 1

;

 

然后按照处理 TABLE 段的步骤继续下面的操作。

 

选项:

如果所有坏块均位于同一个分区,则此时可以采取的一个做法是用一个空表EXCHANGE坏块所在的分区,这可以让应用程序继续运行(无法访问坏块所在的分区中的数据),然后可以从之前的空表中提取任何未损坏的数据。

 

有关其他选项,请参见下面的 TABLE 选项。

 

 

TABLE

– 如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典 TABLE 或 TABLE PARTITION,确定表中存在哪些索引:

例如:

SELECT owner, index_name, index_type

FROM dba_indexes

WHERE table_owner=’&OWNER’

AND table_name=’&SEGMENT_NAME’

;

 

并确定表中是否存在任何主键:

 

Eg: SELECT owner, constraint_name, constraint_type, table_name

FROM dba_constraints

WHERE owner=’&OWNER’

AND table_name=’&SEGMENT_NAME’

AND constraint_type=’P’

;

 

如果存在主键,则确认它是否被任何外键约束引用:

例如:

SELECT owner, constraint_name, constraint_type, table_name

FROM dba_constraints

WHERE r_owner=’&OWNER’

AND r_constraint_name=’&CONSTRAINT_NAME’

;

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典表,可能的选项包括:

恢复

或 抢救表(或分区)中的数据

然后重新创建表(或分区)

或 忽略坏块

(例如:使用 DBMS_REPAIR 标记需要跳过的问题块)

 

 

IOT(索引组织表)

IOT 表中的坏块应按照表或分区表中的处理方式来处理。

唯一的例外是如果 PK 损坏。

IOT 表的 PK 就是表本身,它不能被删除和重新创建。

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典表,可能的选项包括:

恢复

或 抢救表(或分区)中的数据

然后重新创建表(或分区)

或 忽略坏块

(DBMS_REPAIR 不适用于 IOT)

 

 

LOBINDEX

– 确定 LOB 属于哪个表:

 

SELECT table_name, column_name

FROM dba_lobs

WHERE owner=’&OWNER’

AND index_name=’&SEGMENT_NAME’;

 

– 如果表的所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。可能需要恢复数据库。

 

– 不可以重建 LOB 索引,因此您必须将该问题作为受影响的表中 LOB 列上的坏块来处理。

 

使用 ”TABLE“ 部分中的 SQL 语句获取包含损坏的 LOB 索引的表的索引和约束信息,然后返回此处。

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典表,可能的选项包括:

恢复

或 抢救表(及其 LOB 列)中的数据

然后重新创建表

忽略坏块的做法通常不可取,除非不大可能对表中的问题列执行任何进一步的 DML 操作。

 

 

 

 

LOBSEGMENT

 

– 确定 LOB 属于哪个表:

例如:

SELECT table_name, column_name

FROM dba_lobs

WHERE owner=’&OWNER’

AND segment_name=’&SEGMENT_NAME’;

 

– 如果表的所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。可能需要恢复数据库。

 

– 对于非字典表 …

 

使用 ”TABLE“ 部分中的 SQL 语句获取包含坏块的 LOB 数据的表的索引和约束信息,然后返回此处查找具体受影响的行的详细信息。

 

要查找引用损坏的 LOB 块的具体行可能比较困难,因为报告的错误中不会显示表中的哪一行数据包含损坏的 LOB 数据。

 

通常可以参考发生该错误的应用程序日志、任何 SQL_TRACE、会话的10046 跟踪文件(如果有),或通过在会话中设置事件“1578 trace name errorstack level 3”,查看是否有助于标识当前的 SQL/绑定/行。

例如:

ALTER SYSTEM SET EVENTS ‘1578 trace name errorstack level 3’;

 

然后等待应用程序触发该错误,并查找跟踪文件。

 

如果没有任何线索,您可以构建 PLSQL 块,逐行扫描问题表以提取 LOB 列数据,扫描将一直循环进行,直至发生错误。此方法可能需要一段时间,但它应该可以找到引用了损坏的 LOB 块的数据行的主键或 ROWID。

 

例如:

set serverout on

exec dbms_output.enable(100000);

declare

error_1578 exception;

pragma exception_init(error_1578,-1578);

n number;

cnt number:=0;

badcnt number:=0;

begin

for cursor_lob in

(select rowid r, &LOB_COLUMN_NAME L from &OWNER..&TABLE_NAME)

loop

begin

n:=dbms_lob.instr(cursor_lob.L,hextoraw(‘AA25889911’),1,999999) ;

exception

when error_1578 then

dbms_output.put_line(‘Got ORA-1578 reading LOB at ‘||cursor_lob.R);

badcnt:=badcnt+1;

end;

cnt:=cnt+1;

end loop;

dbms_output.put_line(‘Scanned ‘||cnt||’ rows – saw ‘||badcnt||’ errors’);

end;

/

 

 

损坏的 LOB 块可能仅显示为旧版本(为保证一致性读取),且该块未被重新使用,在这种情况下,所有表中所有行都可以访问,但一旦该块被回收重新使用,就不可以插入/更新 LOB 列了。

 

 

选项:

如果所有者为“SYS”,则联系 Oracle Support 并上传所有详细信息。

可能需要恢复数据库。

 

对于非字典表,可能的选项包括:

恢复

或 抢救表(及其 LOB 列)中的数据

然后重新创建表

或 忽略坏块

(不可以在 LOB 段上使用 DBMS_REPAIR)

 

 

 

TEMPORARY

– 如果段类型为 TEMPORARY,则坏块不会影响永久对象。检查发生问题的表空间是否正在被用作 TEMPORARY 表空间:

 

SELECT count(*) FROM dba_users

WHERE temporary_tablespace=’&TABLESPACE_NAME’

;

 

选项:

如果是 TEMPORARY_TABLESPACE,则可能可以创建新的临时表空间,并将所有用户切换到该表空间,然后删除有问题的表空间。

 

如果不是临时表空间,则该块不会再被读取,而且会在下次使用时被重新格式化 — 如果问题的根本原因已经得到解决,则不应再发生该错误。

 

通常情况下,不需要进行任何还原,但如果磁盘可能有问题,且表空间包含有用数据,则最好对数据库中受影响的文件进行恢复。

 

 

 

其他一些段类型

– 如果返回的段类型未包含在上述类型中,则请联系 Oracle Support 并提供迄今为止收集的所有信息,以获得相关建议。

 

 

“无返回行”

– 如果没有包含坏块的 extent,则首先再次检查查询中使用的参数。如果您确定文件号和块编号是正确的,且不属于 DBA_EXTENTS 中的某个对象,则执行以下操作:

 

– 再次检查相关文件是否为临时文件。请注意,临时文件的文件号取决于数据库初始化参数 DB_FILES,因此对该参数的任何更改都会改变错误中报告的绝对文件号。

 

– DBA_EXTENTS 不包含本地管理表空间中用于本地空间管理的块。

 

– 如果您在数据库运行查询语句的时间点与出错的时间点不相同,那么问题对象可能已经被删除,因此针对 DBA_EXTENTS 的查询可能不会显示任何行。

 

– 如果您正在调查的错误由 DBVERIFY 报告,则 DBV 将检查所有块,而不管它们是否属于某个对象。因此,坏块可能存在于数据文件中,但却未被任何对象使用。

 

选项:

未使用的 Oracle 块上的错误可以忽略,因为如果需要使用该块,Oracle 会创建新的块映像(格式化),因此,该块上的任何问题将永不会被读取。

 

如果您怀疑该块可能是空间管理块,则可以使用 DBMS_SPACE_ADMIN 包来帮助您进行检查:

 

exec DBMS_SPACE_ADMIN.TABLESPACE_VERIFY(‘&TABLESPACE_NAME’);

 

以上命令会将不一致写入跟踪文件,但如果遇到致命的坏块,它将报告如下错误:

ORA-03216: Tablespace/Segment Verification cannot proceed

 

位图空间管理块上发生的错误通常可以通过运行以下命令来修正:

 

exec DBMS_SPACE_ADMIN.TABLESPACE_REBUILD_BITMAPS(‘&TABLESPACE_NAME’);

 

 

证据>>

– 对于每个坏块,如果需要尝试并确定实际坏块原因,则收集如下物理证据也是一个比较好的方法:

 

i) 坏块及位于其任意一侧的块的操作系统 HEX 转储。

在 UNIX 上:

dd if=&FILENAME bs=&TS_BLOCK_SIZE skip=&BL-1 count=3 of=BL.dd

^^^^^^^^ ^^^^^^^^^^^^^^ ^^^

例如:对于 BL=1224:

dd if=ts11.dbf bs=4k skip=1223 count=3 of=1223_1225.dd

 

在 VMS 上:

DUMP/BLOCKS=(start:XXXX,end:YYYY)/out=dump.out &FILENAME

 

其中 XXXX=操作系统块编号(512 字节块中)

要计算此值,用报告的块编号乘以“&TS_BLOCK_SIZE/512”。

 

 

ii) 处于 ARCHIVELOG 模式时,复制出错时间前后的归档日志文件的安全副本,最好包括报告错误前数小时的日志文件。并且,保存问题数据文件在出错前的所有副本,因为之前的数据文件映像以及 redo 记录有助于找出错误原因。(DBV 通常可用于检查问题是否存在于文件的备份副本中)。理想的情况是获得没有报告坏块的数据文件备份映像,以及从该时间点开始到首次报告坏块时间之后不久的时段内的所有 redo 记录。

 

iii) 获得问题块的 Oracle 转储:

 

ALTER SYSTEM DUMP DATAFILE ‘&FILENAME’

BLOCK &BL

;

 

(DUMP将生成到 USER_DUMP_DEST 下的跟踪文件)。

 

 

(4) 选择恢复选项

 

现在,最佳的恢复选项取决于受影响的对象。前面第 (3) 部分中的说明应该已经重点介绍了针对每个受影响对象的主要可用选项。选择的实际恢复方法可能包含以下一种或多种混合方法:

 

是否需要进行任何恢复操作?

如果错误发生在 TEMPORARY 表空间中,或位于不再属于任何数据库对象的块中,则无需进行任何操作,尽管将问题表空间重定位到其他存储设备中可能较为明智。请参阅“警告”。

 

可以使用完全恢复吗?

 

要选用完全恢复,必须满足如下条件:

– 数据库处于 ARCHIVELOG 模式(“ARCHIVE LOG LIST”命令显示 Archivelog 模式)

 

– 拥有受影响文件的完好备份。请注意,在某些情况下,坏块可能已经存在,但在很长一段时间内未被发现。如果最近的数据文件备份仍包含坏块,那么只要您拥有所 有必需的归档日志,就可以尝试使用更早的备份。

 

(通常可以使用 DBV START= / END= 选项来检查位于某个备份文件的恢复副本中的特定块是否损坏)

 

– 从备份时间开始到当前时间点的所有归档日志均可用

 

– 当前的在线日志均可用且完好无缺

 

– 错误不是由运行 NOLOGGING 操作之后执行的恢复所导致的

 

如果满足上述条件,完全恢复通常是首选方法

*但请注意*

(a) 如果事务回滚已发现坏块位于对象上,而非 rollback 段本身,则 undo 操作可能已被放弃。在这种情况下,可能需要在恢复完成后重建索引/检查数据完整性。

(b) 如果要恢复的文件包含自上次备份以来执行的 NOLOGGING 操作的数据,在使用了数据文件或数据库恢复的情况下,这些块将被标记为“坏块”。在某些情况下,这会使情况更加糟糕。

 

如果执行数据库恢复后坏块仍然存在,则表示所有备份都包含坏块,底层错误仍存在,或问题通过 redo 重现。在这些情况下,需要选择其他一些恢复选项。

 

请参阅“(4A) 完全恢复”,以了解完全恢复步骤。

如果不需要从对象本身提取任何数据,能否删除或重新创建该对象?

您可以删除对象或从脚本/最近导出的副本重新创建对象。一旦删除一个对象后,该对象中的块将被标记为“空闲”,并且该块在被分配到新对象时将被重新格式 化。明智的做法是,对表进行重命名,而不是删除,除非您完全确定不再需要其中的数据。

 

对于表分区,只需要删除受影响的分区。例如:ALTER TABLE …DROP PARTITION …

如果坏块影响到分区段头,或者包含分区头的文件处于离线状态,则 DROP PARTITION 可能会失败。在这种情况下,首先将其更换为具有相同定义的表,之后仍然可以删除该分区。

例如:ALTER TABLE ..EXCHANGE PARTITION ..WITH TABLE ..;

 

最常见的可重建对象为索引。始终在处理表中的索引问题之前处理表坏块。

有关详细信息,请参阅“(4B) 重建索引”。

 

对于任何段,如果您拥有坏块的绝对文件号和块号,则可使用以下快速提取对象 DDL 的方法:

 

set long 64000

select dbms_metadata.get_ddl(segment_type, segment_name, owner)

FROM dba_extents

WHERE file_id=&AFN

AND &BL BETWEEN block_id AND block_id + blocks -1;

 

是否需要在重新创建对象之前抢救数据?

如果问题位于定期更新的关键应用表上,则可能需要尽可能多地抢救表中数据,然后重新创建该表。

 

有关详细信息,请参阅“(4C) 抢救表中数据”。

 

当前忽略坏块是否可取?

在某些情况下,最直接的选项可能就是忽略坏块,并阻止应用程序对它进行访问。

 

有关详细信息,请参阅“(4D) 忽略坏块”。

 

最后的选项

下列选项是否可行?

将数据库或表空间恢复到较早的时间点(通过时间点恢复)

或还原出现坏块前的冷备份

或使用现有导出文件

有关详细信息,请参阅“(4E) 最后的选项”。

 

 

(4A) 完全恢复

 

如果数据库处于 ARCHIVELOG 模式下,且您拥有受影响文件的完好备份,则恢复通常为首选方法。

这不保证可以解决问题,但的确可以有效的解决大部分坏块问题。如果恢复再次引发问题,则返回到以上选项列表并选择其他方法。

 

如果使用的是 Oracle9i(或更高版本),则可以使用 RMAN BLOCKRECOVER 命令执行块级恢复。

如果使用的是较早版本的 Oracle,则可以执行数据文件恢复(数据库其他部分可以继续运行),或数据库恢复(需要关闭数据库)。

 

如果使用的是 Oracle 11g(或更高版本),则可以使用“Data Recovery Advisor(数据恢复指导)”(请参阅 Note 1317849.1)

 

 

 

块级恢复

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

自 Oracle9i 版本起,RMAN 允许恢复单个块,同时数据库的其他部分(包括数据文件中的其他块)仍可以进行正常访问。请注意,块级恢复只能将块完全恢复到当前时间点。

 

要使用此选项恢复单个块,不一定要使用 RMAN 进行备份。

例如:

实际情况是,文件 6 的块 30 上发生 ORA-1578 错误,可能是由于介质问题导致的坏块,且您拥有该文件的完好冷备份映像,并已还原到“…/RESTORE/filename.dbf”。

假设所有归档日志均存在(位于默认位置),则可以通过 RMAN 使用以下命令序列执行块级恢复:

 

rman nocatalog

connect target

catalog datafilecopy ‘…/RESTORE/filename.dbf’;

run {blockrecover datafile 6 block 30;}

 

此操作将使用注册的数据文件备份映像和任何需要的归档日志来执行块恢复,仅将有问题的块恢复到当前时间点。

 

有关 RMAN BLOCKRECOVER 命令和限制的所有详细信息,请参阅文档 note 144911.1。

 

 

 

数据文件恢复

~~~~~~~~~~~~~~~~~~

数据文件恢复包括下列步骤。如果有多个文件,则针对每个文件重复执行这些步骤,或参阅下面的“数据库恢复”。当数据库处于 OPEN 或 MOUNTED 状态时,均可使用这些步骤。

 

使受影响的数据文件离线

例如:ALTER DATABASE DATAFILE ‘name_of_file’ OFFLINE;

 

将文件复制到安全位置(以防备份损坏)

 

将文件的最新备份还原到完好的磁盘上

 

使用 DBVERIFY 检查还原的文件是否有坏块

有关使用 DBVERIFY 的详细信息,请参阅 Document 35512.1

 

假设还原的文件完好,则将数据文件重命名并保存到新位置(如果不是原来的位置)

例如:ALTER DATABASE RENAME FILE ‘old_name’ TO ‘new_name’;

 

恢复数据文件

例如:RECOVER DATAFILE ‘name_of_file’;

 

使数据文件上线

例如:ALTER DATABASE DATAFILE ‘name_of_file’ ONLINE;

 

 

数据库恢复

~~~~~~~~~~~~~~~~~

数据库恢复通常包含以下步骤:

 

关闭数据库(使用选项 immediate 或 abort)

 

将待恢复的所有文件的当前副本复制到安全位置

 

将备份文件还原到完好的磁盘上

请勿还原控制文件或在线 REDO 日志文件

 

使用 DBVERIFY 检查还原的文件

有关使用 DBVERIFY 的详细信息,请参阅 Document 35512.1

 

启动数据库到MOUNT状态(startup mount)

 

对任何需要重新定位的数据文件进行重命名

例如:ALTER DATABASE RENAME FILE ‘old_name’ TO ‘new_name’;

 

确保所有必需的文件在线

例如:ALTER DATABASE DATAFILE ‘name_of_file’ ONLINE;

 

恢复数据库

例如:RECOVER DATABASE

 

打开数据库

例如:ALTER DATABASE OPEN;

 

 

完全恢复后

~~~~~~~~~~~~~~~~~~~~~~~~~~

一旦执行了完全恢复,最好在允许使用之前先检查数据库:

 

– 针对每个问题对象运行“ANALYZE <table_name> VALIDATE STRUCTURE CASCADE”,检查表/索引是否存在不匹配。如果有任何 undo 操作曾被放弃,此命令可能会显示不匹配,此时需要重建索引。

 

– 在应用程序级别检查表中数据的逻辑完整性。

 

 

 

(4B) 重建索引

 

损坏对象为用户索引时,如果底层表没有损坏,则可以删除并重建该索引。

如果底层表也已经损坏,则应在重建任何索引之前先解决该表的坏块。

 

如果收集的信息表示索引有从属外键约束,则需要执行以下操作:

 

– ALTER TABLE <child_table> DISABLE CONSTRAINT <fk_constraint>;

对于每个外键

 

– 使用以下命令重建主键

ALTER TABLE <table> DISABLE CONSTRAINT <pk_constraint>;

DROP INDEX <index_name>;

CREATE INDEX <index_name> .. with appropriate storage clause

ALTER TABLE <table> ENABLE CONSTRAINT <pk_constraint>;

 

– 启用外键约束

ALTER TABLE <child_table> ENABLE CONSTRAINT <fk_constraint>;

 

对于索引分区,可以执行以下命令:

ALTER INDEX …REBUILD PARTITION …;

 

注意:

(1) 不要使用“ALTER INDEX .. REBUILD”命令重建损坏的非分区索引,这一点非常重要,因为此操作通常会尝试从包含坏块的现有索引段中构建新索引。

“ALTER TABLE … REBUILD ONLINE”和“ALTER INDEX … REBUILD PARTITION …”不会从旧索引段中构建新索引,因此可以使用。

 

(2) 如果新索引包含的列为现有索引的子集,则 Create INDEX 可以使用现有索引中的数据。因此,如果您有两个损坏的索引,应在重建之前将两个都删除。

 

(3) 重建索引时,请确保使用正确的存储选项。

 

 

(4C) 抢救表中数据

 

如果损坏的对象为 TABLE 或 CLUSTER 或 LOBSEGMENT,则您必须明白,坏块内的数据已经丢失。部分数据可能可以从块的 HEX 转储中,或从索引涵盖的列中抢救回来。

 

重要信息:

由于可能需要从索引中抢救坏块中的数据,因此最好不要删除任何现有索引,直至所有需要的数据提取完成。

 

从包含坏块的表中提取数据有多种方法。选择最恰当的方法,详细信息如下所述。这些方法的目的是从可访问的表块中提取尽可能多的数据。通常,将损坏的表重命名是一个比较好的方法,这样就可以使用正确的名称创建新对象。

例如:RENAME <emp> TO <emp_corrupt>;

 

从坏块表中提取坏块周围数据的方法

 

(1) 从 Oracle 7.2 开始(包括 Oracle 8.0、8.1 和 9i),可以跳过表中的坏块。

这是到目前为止最简单的提取表数据的方法,此方法在以下文档中做了讨论:

 

Document 33405.1 – Extracting data using DBMS_REPAIR.SKIP_CORRUPT_BLOCKS or Event 10231

 

如果坏块位于 IOT overflow 段,则应使用相同的方法,不同的是使用 Event 10233 和全索引扫描。

 

请注意,此方法只适用于块的“包装”已被标记为“坏块”的情况。例如:如果块报告 ORA-1578 错误。

 

如果问题为 ORA-600 或其他非ORA-1578 错误,则通常可以使用 DBMS_REPAIR 将表中坏块标记为“软坏块”。这样在您访问该数据块时,系统将显示 ORA-1578 错误,从而可以使用 DBMS_REPAIR.SKIP_CORRUPT_BLOCKS。

 

注意:被“FIX_CORRUPT_BLOCKS”程序标记为“坏块”的块在任何还原/恢复操作之后还将被标记为“坏块”.

 

有关使用 DBMS_REPAIR 进行此操作的全部详细信息,请参阅相关文档,但概括起来说,步骤如下:

 

– 使用 DBMS_REPAIR.ADMIN_TABLES 创建管理表

– 使用 DBMS_REPAIR.CHECK_OBJECT 找到问题块

– 在损坏问题块之前将其中所有完好的数据导出。

– 使用 DBMS_REPAIR.FIX_CORRUPT_BLOCKS 将找到的问题块标记为“坏块”,然后它们就会显示 ORA-1578

– 如果需要,使用 DBMS_REPAIR.SKIP_CORRUPT_BLOCKS 跳过表中的坏块。

 

 

(2) 从 Oracle 7.1 开始,可以使用 ROWID 范围扫描。此功能的语法较为复杂,但可以使用 ROWID 提示选择坏块周围的数据。由于 Oracle7 和 Oracle8 中的 ROWID 格式有所不同,故有两篇文章分别介绍此功能:

 

Document 61685.1 – Using ROWID Range Scans to extract data in Oracle8 and higher

Document 34371.1 – Using ROWID Range Scans to extract data in Oracle7

 

 

(3) 如果存在主键,则可以通过此索引选择表数据。也可以通过任何其他索引选择一些数据。此方法较慢,花费时间较长,通常只有 Oracle 7.0 版本才使用。此方法在 Document 34371.1 中进行了介绍(此外,还介绍了 ROWID 范围扫描)

 

(4) 有多种抢救程序/PLSQL 脚本可用于抢救表中的数据。与上述方法相比,这些方法在设置和使用方面需要花费更长的时间,但常常能够处理除 ORA-1578 之外的各类坏块。由于这些方法通常需要由技术支持人员给予大量的亲身指导,因此客户可能看不到关于这些方法的部分文章。

 

要使用以下程序,需要使用 Pro*C,且需要了解如何构建 Pro*C 可执行文件:

Document 2077307.6 – SALVAGE.PC for Oracle7

 

要使用以下程序,需要手动交互:

Document 2064553.4 – SALVAGE.SQL for Oracle7/8

 

 

 

从包含损坏的 LOBSEGMENT 块的表中提取数据的方法

 

在 LOB 段上不可以使用 DBMS_REPAIR。

 

如果坏块 LOB 块未被表中的任何行引用,则应该可以使用 CREATE TABLE as SELECT (CTAS) 来按选择创建表,或按原样导出/删除/导入该表。

如果坏块 LOB 块被某个行引用,则应该可以使用不包括问题行的 WHERE 谓词进行选择或导出

 

警告:

可以将问题行的 LOB 列值更新为 NULL,从而使 SELECT 操作不再返回ORA-1578 错误 *但是* 坏块将等待被重新使用,随着对行中的 LOB 列进行 INSERT 或 UPDATE 操作,当有问题的块被重新使用时,最后还是会报ORA-1578错误,那时的情况比已知行出现坏块更糟糕。因此,只有您打算立刻重新创建表,才应该将 LOB 列设为 NULL。

 

 

从坏块本身提取数据

 

由于坏块本身已经“损坏”,则从该块中提取的任何数据都应被视为可疑数据。从坏块本身获取数据行的主要方法包括:

 

– 对于 TABLE 的块,Oracle Support 可以使用一款尝试解释块内容的工具。

 

– 使用表中现有索引,利用落在坏块内的ROWID 来提取索引所涵盖的列数据,上文提到的 ROWID 范围扫描文章在接近结束时对此内容有所介绍:

对于 Oracle8/8i,请参阅 Document 61685.1

对于 Oracle7,请参阅 Document 34371.1

 

– 在 redo 流上可以使用 LogMiner 来查找向问题块加载数据的初始插入/更新操作。此处的主要因素是数据实际被放入问题块的时间。

例如,行 2 可能在昨天已插入,而行 1 可能在 5 年前已插入。

 

 

 

(4D) 忽略坏块

 

出错时可以忽略坏块并接受报告的错误,或在应用程序级别阻止对出问题的块行进行访问。

例如:如果问题块/行位于子表中,则可以在应用程序级别阻止对父表中对应行的访问,从而子行就永不会被访问。(但要注意级联类约束)

 

这 样做可能不利于批量访问数据的报告和其他任务,因此,为了阻止块在被访问时报错,前面 4C 中所述的 DBMS_REPAIR 选项也不失为一个可取的方法。使用这种方法标记并跳过坏块提供了一种短期的解决方案,从而在计划停机时可以尝试进行完全数据抢救和/或恢复,或留出更多时 间在第二个(克隆)数据库上尝试其他恢复选项。但请注意,使用 DBMS_REPAIR.FIX_CORRUPT_BLOCKS 标记块坏块将导致标记的块在恢复后还是“坏块”。

 

忽略坏块对于快速老化且即将被清除的数据而言是比较好的选择(例如,在按日期分区的表中,较老的分区将在某时间点被删除)。

 

 

忽略 LOB 段上的坏块

 

在应用程序级别,可以忽略损坏的 LOB 列,直到可以重新构建该表。

 

确保不出现上述“警告”中的情形的一种方法是,确保应用程序只能通过表上的包含WHERE 谓词的视图来访分表中的数据。

例如:假设表 MYTAB(a number primary key,b clob)有一行或多行指向损坏的 LOB 数据。

 

ALTER TABLE MYTAB ADD ( BAD VARCHAR2(1) );

 

CREATE VIEW MYVIEW AS SELECT a,b FROM MYTAB WHERE BAD is null;

 

对任何问题行设置 BAD=’Y’

 

如果只通过 MYVIEW 访问 MYTAB,该行将永不可见,因此也无法更新,从而实现了坏块条目隔离,直到问题解决。

 

很明显,此示例更多的是一个设计时解决方案,但某些应用程序可能已有类似机制,且可能只通过某个视图(或通过 RLS 策略)访问数据,从而提供某些选项来隐藏问题行。

 

 

针对忽略坏块的警告

虽然可以忽略坏块,但需要注意的是,坏块在运行 DBVERIFY、RMAN 备份时仍然会以警告/错误等形式出现。

 

请务必仔细记录您将在这些工具中看到的任何坏块,尤其是您期望在使用 RMAN 时跳过的任何块(例如,设置了 MAX_CORRUPT),并确保在清除坏块后移除任何对错误的“接受”选项。

 

例如:假设坏块已处理为忽略坏块,并在应用程序级别跳过问题行。

RMAN 可能被配置为在备份时接受坏块。然后在稍后的表重组期间重新创建表。

如果 RMAN 配置未及时更新以反映目前已无任何错误,则 RMAN 可能会忽略稍后出现的某些其他坏块。

 

此外,还有重要的一点需要注意,忽略 table 段中的坏块可能导致查询返回不一致的结果。

例如:设置了 SKIP_CORRUPT 的表可能出现不同的结果,具体取决于是使用了了索引扫描还是表访问。

其他报告可能只是报错。

 

请注意,如果忽略坏块但使用 DBMS_REPAIR.FIX_CORRUPT_BLOCKS 标记,系统会向坏块中写入 redo 信息,这可能会限制后续的恢复选项。

 

 

(4E) 最后的选项

 

如果你有standby环境(物理或逻辑),请首先对其进行检查。

 

无论问题发生在何种类型的块上,均可使用一种可能的选项,即将数据库或问题表空间恢复到出现坏块之前的某个时间点。此选项的困难之处在于,并不总能知道问题首次出现的时间。

 

DBVERIFY 通常可用于检查还原的文件是否存在坏块。有关使用 DBVERIFY 的详细信息,请参阅 Document 35512.1。尤其是,START= / END= DBV 选项可用于在还原的备份映像上快速进行首次测试,以检查问题块本身是否出错。

 

本部分列出了一些可用于进行恢复操作的最终选项。如果您看到这里,则必定发生了以下一种或多种情况:

 

– 您丢失了非常重要的数据文件(或数据文件出现坏块),而没有问题文件的正常备份(无坏块)

– 既不处于 ARCHIVELOG 模式,也没有自文件创建以来的全部归档日志

– 完全恢复后仍重复出现问题

 

 

最后的机会:

请注意,如果您丢失了数据文件的所有副本,但仍具有自文件创建以来的全部归档日志,则仍有可能恢复该文件。

例如:

ALTER DATABASE CREATE DATAFILE ‘….'[as ‘…’] ;

RECOVER DATAFILE ‘….’

ALTER DATABASE DATAFILE ‘….’ONLINE;

 

如果您遇到这种情况,请在继续下面的操作之前先尝试使用这些步骤来恢复数据文件。

 

 

如果您到达这一步,就说明没有其他办法可以将文件恢复到当前时间点。此时最好关闭实例,并对当前数据库进行备份,以便在选用的措施失败后仍然能够回退到当前时间点。(例如:如果发现备份坏块)。

 

可用的一些选项概述如下:

 

恢复到早期的冷备份

– 例如:如果处于 NOARCHIVELOG 模式

 

从冷备份建立克隆数据库

– 并提取(导出)问题表

或传输问题表空间

 

使用基于时间点的恢复将数据库恢复到一致的时间点

– 需要完好备份和任何所需的归档日志

– 必须还原所有文件且将整个数据库前滚到恰当的时间点。

– 可以在克隆数据库中执行基于时间点的恢复,然后将问题表空间传输到问题数据库,或将问题表利用导出/导入工具从克隆数据库导入到问题数据库。

 

表空间基于时间点的恢复

– 可以仅对受影响的表空间执行基于时间点的恢复。许多文档均对表空间基于时间点的恢复做了介绍,如 Document 223543.1。

 

 

 

从逻辑导出/副本重新创建数据库

– 需要具有完好的数据库逻辑备份

– 注意:要使用此选项,必须重新创建数据库。

– 与其他选项一样,可以在克隆数据库中进行重新创建,只为获得问题表的完好映像。

 

 

如果已具有完好备份,使用 DB_BLOCK_CHECKING=TRUE 进行前滚将有助于找到首次出错的时间点。在调查恢复选项时,通常不需要关闭问题数据库。

例如:可以只将系统表空间和问题表空间数据文件还原到完全不同的位置和/或机器,作为不同的实例,以便于调查可以前滚到多久以前的时间点等。

自 Oracle9i 起,您还可以使用“试验恢复”选项来让自己摆脱一边研究选项一边必须不断还原备份的情形。

 

 

 

转自 MOS文档 1526911.1

沪ICP备14014813号-2

沪公网安备 31010802001379号