ORACLE坏块(ORA-01578)处理方法

阅读更多
0.模拟出现坏块环境

SQL> CREATE TABLESPACE "TEST"
  2      LOGGING
  3      DATAFILE 'D:\ORACLE\ORADATA\ALAN\TEST.ora' SIZE 1M EXTENT
  4      MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT  AUTO
  5  /

表空间已创建。

SQL> create user alantest identified by alantest
  2  default tablespace test
  3  temporary tablespace temp;

用户已创建

SQL> grant dba to alantest;

授权成功。

SQL> connect alantest/alantest
已连接。
SQL> show user
USER 为"ALANTEST"
SQL> create table test_alan as select * from dba_objects;

RMAN> backup database format='c:\rmanbackup\FULL_%U';

启动 backup 于 07-7月 -06
正在使用目标数据库控制文件替代恢复目录
分配的通道: ORA_DISK_1
通道 ORA_DISK_1: sid=18 devtype=DISK
通道 ORA_DISK_1: 正在激活 full 数据文件备份集
通道 ORA_DISK_1: 正在指定备份集中的数据文件
在备份集中包含当前的 SPFILE
备份集中包括当前控制檔
输入数据文件 fno=00001 name=D:\ORACLE\ORADATA\ALAN\SYSTEM01.DBF
输入数据文件 fno=00002 name=D:\ORACLE\ORADATA\ALAN\UNDOTBS01.DBF
输入数据文件 fno=00003 name=D:\ORACLE\ORADATA\ALAN\INDX01.DBF
输入数据文件 fno=00005 name=D:\ORACLE\ORADATA\ALAN\USERS01.DBF
输入数据文件 fno=00004 name=D:\ORACLE\ORADATA\ALAN\TOOLS01.DBF
输入数据文件 fno=00006 name=D:\ORACLE\ORADATA\ALAN\CATTAB01.DBF
输入数据文件 fno=00007 name=D:\ORACLE\ORADATA\ALAN\TEST.ORA
通道 ORA_DISK_1: 正在激活段 1 于 07-7月 -06
通道 ORA_DISK_1: 已完成段 1 于 07-7月 -06
段 handle=C:\RMANBACKUP\FULL_0JHNJ63P_1_1 comment=NONE
通道 ORA_DISK_1: 备份集已完成, 经过时间:00:01:38
完成 backup 于 07-7月 -06

SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。



使用编辑工具修改数据文件 生成坏块



C:\>dbv  file=D:\ORACLE\ORADATA\ALAN\TEST.ora blocksize=8192



DBVERIFY: Release 9.2.0.6.0 - Production on 星期五 7月 7 14:53:46 2006

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

DBVERIFY - 验证正在开始 : FILE = D:\ORACLE\ORADATA\ALAN\TEST.ora
标记为损坏的页12
***
Corrupt block relative dba: 0x01c0000c (file 7, block 12)
Bad check value found during dbv:
Data in bad block -
type: 6 format: 2 rdba: 0x01c0000c
last change scn: 0x0000.0007bc14 seq: 0x1 flg: 0x04
consistency value in tail: 0xbc140601
check value in block header: 0x91ea, computed block checksum: 0x110e
spare1: 0x0, spare2: 0x0, spare3: 0x0
***
DBVERIFY - 验证完成

检查的页总数         :128
处理的页总数(数据):77
失败的页总数(数据):0
处理的页总数(索引):0
失败的页总数(索引):0
处理的页总数(其它):16
处理的总页数 (段)  : 0
失败的总页数 (段)  : 0
空的页总数            :34
标记为损坏的总页数:1
汇入的页总数           :0
Highest block SCN            : 506930 (0.506930)



1.对相关的表进行操作:

SQL> select * from alantest.test_alan where rownum=1234;
select * from alantest.test_alan where rownum=1234
*
ERROR 位于第 1 行:
ORA-01578: ORACLE 数据块损坏(文件号7,块号12)
ORA-01110: 数据文件 7: 'D:\ORACLE\ORADATA\ALAN\TEST.ORA'

SQL> select count(*) from alantest.test_alan;
select count(*) from alantest.test_alan
                              *
ERROR 位于第 1 行:
ORA-01578: ORACLE 数据块损坏(文件号7,块号12)
ORA-01110: 数据文件 7: 'D:\ORACLE\ORADATA\ALAN\TEST.ORA'



2. 检查alter.log出现的报错信息



Corrupt block relative dba: 0x01c0000c (file 7, block 12)
Bad check value found during buffer read
Data in bad block -
type: 6 format: 2 rdba: 0x01c0000c
last change scn: 0x0000.0007bc14 seq: 0x1 flg: 0x04
consistency value in tail: 0xbc140601
check value in block header: 0x91ea, computed block checksum: 0x91a
spare1: 0x0, spare2: 0x0, spare3: 0x0


3. 确定坏块的对象是什么?

SQL> select tablespace_name,segment_type,segment_name,owner from dba_extents
  2  where file_id=7 and 12 BETWEEN block_id AND block_id+blocks-1;

  TABLESPACE_NAME  SEGMENT_TYPE  SEGMENT_NAME     OWNER

  TEST                             TABLE         TEST_ALAN           ALANTEST



a. file#  block# 可以从错误信息中得到
b. 如果是temp檔中出现坏块 是没有记录返回的
c. 通常容易出现坏块的对象有:
   数据字典(system表空间) 回滚段临时段 联机日志 索引或者分区索引 表



4. 如果通过返回的结果确定坏的是索引段只要把这个索相删除然后重建一下就可以了
   如果出现坏的是表段请继续往下看



5. 具体解决办法



A. 恢复数据文件方法



前提要求:

a.数据库为归档方式
b.有完整的物理备份

步骤:
(1) offline受影响的数据文件 执行以下的语句:

    ALTER DATABASE DATAFILE 7 OFFLINE;

(2) 把旧的数据文件保存后移除

(3) 恢复数据文件 执行以下语句:

    RESTORE DATAFILE 7;
    RECOVER DATAFILE 7;

(4) Online恢复后的数据文件 执行以下的语句:

    ALTER DATABASE DATAFILE 7 ONLINE;


B.只恢复坏的block(9i以上版本可用)



前提要求:

a.要求数据库版本是9.2.0以上
b.数据库为归档方式
c.有完整的物理备份

步骤:

(1) 使用RMAN的BLOCKRECOVER命令:

    Rman>run{
         blockrecover datafile 7 block 12;
         }

RMAN> blockrecover datafile 7 block 12;

启动 blockrecover 于 07-7月 -06
正在使用目标数据库控制文件替代恢复目录
分配的通道: ORA_DISK_1
通道 ORA_DISK_1: sid=16 devtype=DISK


通道 ORA_DISK_1: 正在恢复块
通道 ORA_DISK_1: 正在指定要从备份集恢复的块
正在恢复数据文件 00007 的块
通道 ORA_DISK_1: 已从备份段 1 恢复块
段 handle=C:\RMANBACKUP\FULL_0JHNJ63P_1_1 tag=TAG20060707T132408 params=NULL
通道 ORA_DISK_1: 块恢复已完成

正在开始介质的恢复
完成介质的恢复

完成 blockrecover 于 07-7月 -06

    可以强制使用某个SCN号之前的备份来恢复数据块
    Rman>run{
         blockrecover datafile 7 block 12 restore until sequence 8505;
         }



C.通过ROWID RANGE SCAN 保存资料



步骤:
(1) 通过file# block# 查出出现坏块的object

SQL> select tablespace_name,segment_type,segment_name,owner from dba_extents
  2  where file_id=7 and 12 BETWEEN block_id AND block_id+blocks-1;

  TABLESPACE_NAME  SEGMENT_TYPE SEGMENT_NAME OWNER

  TEST             TABLE        TEST_ALAN    ALANTEST

(2) 构造坏块的ROWID

SQL> SELECT DATA_OBJECT_ID FROM dba_objects where object_name='TEST_ALAN' and owner='ALANTEST';

DATA_OBJECT_ID
--------------
          6627

SQL> SELECT dbms_rowid.rowid_create(1,6627,7,12,0) from DUAL;

DBMS_ROWID.ROWID_C
------------------
AAABnjAAHAAAAAMAAA

SQL> SELECT dbms_rowid.rowid_create(1,6627,7,13,0) from DUAL;

DBMS_ROWID.ROWID_C
------------------
AAABnjAAHAAAAANAAA



Oracle的物理扩展ROWID有18位 每位采用64位编码分别用A~Z、a~z、0~9、+、/共64个字符表示
A表示0,B表示1,……Z表示25,a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63 .

ROWID具体划分可以分为4部分。

1.OOOOOO:前6位表示DATA OBJECT NUMBER,将起转化位数字后匹配DBA_OBJECTS中的DATA_OBJECT_ID,可以确定表信息。
2.FFF:第7到9位表示相对表空间的数据文件号。
3.BBBBBB:第10到15位表示这条记录在数据文件中的第几个BLOCK中。
4.RRR:最后3位表示这条记录是BLOCK中的第几条记录


  一个数据块坏了,那么这个数据块里的资料全部都不能读了
  因为读取数据的最小单位是block 所以应该是这个BLOCK中第0条记录开始



FUNCTION ROWID_CREATE RETURNS ROWID
参数名称                       类型                    输入/输出默认值?
------------------------------ ----------------------- ------ --------
ROWID_TYPE                     NUMBER                  IN
OBJECT_NUMBER                  NUMBER                  IN
RELATIVE_FNO                   NUMBER                  IN
BLOCK_NUMBER                   NUMBER                  IN
ROW_NUMBER                     NUMBER                  IN

如果对ROWID的结构不是很清楚 可以参考这个 http://blog.itpub.net/post/468/11046



(3) 建一个临时表 保存未损坏的资料



CREATE TABLE alantest.test_alan_bak AS SELECT * FROM alantest.test_alan Where 1=2;

SQL> INSERT INTO alantest.test_alan_bak SELECT /*+ ROWID(A) */ * FROM alantest.t
est_alan A WHERE rowid < 'AAABnjAAHAAAAAMAAA';

已创建0行。

SQL> INSERT INTO alantest.test_alan_bak SELECT /*+ ROWID(A) */ * FROM alantest.t
est_alan A WHERE rowid >= 'AAABnjAAHAAAAANAAA';

已创建6281行。



(4) 更改表名

    SQL>alter table alantest.test_alan rename to alantest.test_alan_0607;

    SQL>alter table alantest.test_alan_bak rename to alantest.test_alan;


(5) 根据临时表中的数据重构表 重建表上的索引 限制


D.  使用10231诊断事件 在做全表扫描的时候跳过坏块



步骤:

(1) 设置参数

    可以在session级别设定:

    SQL> ALTER SESSION SET EVENTS '10231 TRACE NAME CONTEXT FOREVER, LEVEL 10';

    会话已更改。

    也可以在数据库级别上设定 在初始化参数中加入

    event="10231 trace name context forever, level 10" 然后重启数据库
    或者
    ALter system set event '10231 TRACE NAME CONTEXT FOREVER, LEVEL 10';

(2) 创建一个临时表tmp_table把原始表中除坏块以外的资料都检索出来

    SQL> CREATE TABLE alantest.tmp_table as select * from alantest.test_alan;

    表已创建。

    SQL> select count(*) from alantest.test_alan;

    COUNT(*)
    ----------
      6281

(3) 更改表名

    SQL>alter table alantest.test_alan rename to alantest.test_alan_0607;

    SQL>alter table alantest.tmp_table rename to alantest.test_alan;

(4) 在新source_table上重新创建索引 约束 授权 trigger等对象

(5) 利用表之间的业务关系 把坏块中的数据补足


E.  使用dbms_repair包标记有坏块的表 在做全表扫描的时候跳过坏块



(1) 执行以下的语句:

    SQL> connect / as sysdba
    已连接。
    SQL> Execute DBMS_REPAIR.SKIP_CORRUPT_BLOCKS('ALANTEST','TEST_ALAN');

    PL/SQL 过程已成功完成。

  
    PROCEDURE SKIP_CORRUPT_BLOCKS
    参数名称                       类型                    输入/输出默认值?
    ------------------------------ ----------------------- ------ --------
    SCHEMA_NAME                    VARCHAR2                IN
    OBJECT_NAME                    VARCHAR2                IN
    OBJECT_TYPE                    BINARY_INTEGER          IN     DEFAULT
    FLAGS                          BINARY_INTEGER          IN     DEFAULT



(2) 使用exp工具或者create table as select的方法取出没有坏块数据

    SQL> create table alantest.aaa as select * from alantest.test_alan;

    表已创建。

    SQL> select count(*) from alantest.aaa;

    COUNT(*)
    ----------
      6281

(3) 重建表 表上的索引 约束 授权 trigger等对象


6. 如何检查数据库中是否存在坏块



1. ANALYZE TABLE tablename VALIDATE STRUCTURE CASCADE 同时校验表与索引

   它执行坏块的检查但是不会标记坏块为corrupt 检测的结果保存在USER_DUMP_DEST目录下的用户trace文件中



2. 使用RMAN

   RMAN> connect target /

   连接到目标数据库: ALAN (DBID=1282599050)

   RMAN> backup validate datafile 7;

   启动 backup 于 07-7月 -06
   正在使用目标数据库控制文件替代恢复目录
   分配的通道: ORA_DISK_1
   通道 ORA_DISK_1: sid=18 devtype=DISK
   通道 ORA_DISK_1: 正在激活 full 数据文件备份集
   通道 ORA_DISK_1: 正在指定备份集中的数据文件
   输入数据文件 fno=00007 name=D:\ORACLE\ORADATA\ALAN\TEST.ORA
   通道 ORA_DISK_1: 备份集已完成, 经过时间:00:00:04
   完成 backup 于 07-7月 -06

   检查alert.log

   ***
   Corrupt block relative dba: 0x01c0000c (file 7, block 12)
   Bad check value found during backing up datafile
   Data in bad block -
   type: 6 format: 2 rdba: 0x01c0000c
   last change scn: 0x0000.0007bc14 seq: 0x1 flg: 0x04
   consistency value in tail: 0xbc140601
   check value in block header: 0x91ea, computed block checksum: 0x110e
   spare1: 0x0, spare2: 0x0, spare3: 0x0
   ***
   Reread of blocknum=12, file=D:\ORACLE\ORADATA\ALAN\TEST.ORA. found same corrupt data

   查询v$database_block_corruption视图
 
   SQL> select * from v$database_block_corruption where file#=7;

     FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTIO
    ---------- ---------- ---------- ------------------ ---------
         7         12          1                  0 FRACTURED

你可能感兴趣的:(Oracle,SQL,数据结构,C,C++)