block corruption
块折断(也称为块损坏)
块折断分为两种:硬折断和软折断。
硬折断:数据块的格式已经不是ORACLE数据块的格式了,如磁道损害,磁道被重新擦写等。
软折断:数据块的格式还是ORACLE的格式,但数据块内部出现了数据不一致的情况。也就是数据块内部无法经过正常的校验。这种情况常常是BUG引起的,或程序错误、异常导致数据块的数据不一致的情况。有些数据块可能在内存中就有问题,所以写到磁盘上的数据后就会有问题。
软折断的模拟
单独创立一个表空间:
SQL> create tablespace lxtbs01 datafile 'd:\u01\app\oracle\oradata\prod\lxtbs01.dbf' size 50m;
表空间已创建。
SQL>
在表空间上建立一个表:
SQL> create table tt tablespace lxtbs01 as select * from dba_objects;
表已创建。
SQL> insert into tt select * from tt;
已创建72462行。
SQL> commit;
提交完成。
SQL>
将表空间对应的数据文件用UE打开并修改
如何悠:将光标移动到要修改的字符上,先按r再按要替换成的字符就行了,注意数据格式为16进制,数字为0到9,字母为A到F
如果深入研究,则可以做到想让哪个块损坏就让哪个坏损坏
修改完成后保存并关闭后重新启动数据库,发现可以正常启来
SQL> shutdown immediate;
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup
ORACLE 例程已经启动。
Total System Global Area 855982080 bytes
Fixed Size 2180544 bytes
Variable Size 503319104 bytes
Database Buffers 348127232 bytes
Redo Buffers 2355200 bytes
数据库装载完毕。
数据库已经打开。
SQL>
SQL> select file#,checkpoint_change# from v$datafile;
FILE# CHECKPOINT_CHANGE#
---------- ------------------
1 1090854
2 1090854
3 1090854
4 1090854
5 1090854
6 1090854
已选择6行。
SQL> select file#,checkpoint_change# from v$datafile_header;
FILE# CHECKPOINT_CHANGE#
---------- ------------------
1 1090854
2 1090854
3 1090854
4 1090854
5 1090854
6 1090854
已选择6行。
SQL>
能正常启动,是因为修改的不是数据文件的最前面的8个块(头8个块是打开数据库前要检查的,存放的是数据文件SCN等相关重要信息),数据文件满足打开数据库的一些条件。但查询数据文件里面涉及的表就会报错了,如下:
SQL> select count(*) from tt;
select count(*) from tt
*
第 1 行出现错误:
ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 307)
ORA-01110: 数据文件 6: 'D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF'
查询数据时检测到数据文件有坏块,直接抛出第一个坏块的信息,修复好第一个块后会抛出第二个坏块的信息
检测数据块是否完好,ORACLE提供了以下方法:
DBVERIFY其实就是DBV命令
当用命令检查的时候也会报错:
SQL> analyze table tt validate structure;
analyze table tt validate structure
*
第 1 行出现错误:
ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 307)
ORA-01110: 数据文件 6: 'D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF'
加上关键字cascade表示不仅检查表tt,还同时检查和tt表相关的其他所有对象
SQL> analyze table tt validate structure cascade;
analyze table tt validate structure cascade
*
第 1 行出现错误:
ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 307)
ORA-01110: 数据文件 6: 'D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF'
SQL>
如果是没有问题的表,则检查时就不会报错:
SQL> analyze table scott.emp validate structure cascade;
表已分析。
SQL>
操作系统提供了一个命令dbv来检查数据文件是否损坏
FILE指定要检测的文件
START 和 END 是当数据文件比较大的时候可以指定起始块和结束块
BLOCKSIZE指定数据文件块大小,默认是8192也就是8K,如果不是数据文件数据块不是默认8K则要指定这个参数
LOGFILE 将输出日志 保护到一个文件里面
FEEDBACK:显示进度条
PARFILE:检测的命令可以放到一个参数文件里面,可以直接调用参数文件来做检测,而不用每次都输入命令
USERID SEGMENT_ID HIGH_SCN 可以直接检查某用户的某个段
用dbv验证可以得出哪些数据块损坏:
这里的页也就是指数据块
如果数据文件不是标准块,要指定blocksize参数,否则会报错:
rdba:relative data block address
在备份前检测数据块有没有损坏:
RMAN> backup validate database;
启动 backup 于 17-1月 -13
使用目标数据库控制文件替代恢复目录
分配的通道: ORA_DISK_1
通道 ORA_DISK_1: SID=39 设备类型=DISK
分配的通道: ORA_DISK_2
通道 ORA_DISK_2: SID=32 设备类型=DISK
通道 ORA_DISK_1: 正在启动压缩的全部数据文件备份集
通道 ORA_DISK_1: 正在指定备份集内的数据文件
输入数据文件: 文件号=00001 名称=D:\U01\APP\ORACLE\ORADATA\PROD\SYSTEM01.DBF
输入数据文件: 文件号=00004 名称=D:\U01\APP\ORACLE\ORADATA\PROD\USERS01.DBF
输入数据文件: 文件号=00006 名称=D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF
通道 ORA_DISK_2: 正在启动压缩的全部数据文件备份集
通道 ORA_DISK_2: 正在指定备份集内的数据文件
输入数据文件: 文件号=00002 名称=D:\U01\APP\ORACLE\ORADATA\PROD\SYSAUX01.DBF
输入数据文件: 文件号=00005 名称=D:\U01\APP\ORACLE\ORADATA\PROD\EXAMPLE01.DBF
输入数据文件: 文件号=00003 名称=D:\U01\APP\ORACLE\ORADATA\PROD\UNDOTBS01.DBF
通道 ORA_DISK_1: 备份集已完成, 经过时间:00:00:15
数据文件列表
=================
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
1 OK 0 13973 88326 1093073
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\SYSTEM01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 58640
索引 0 12174
其他 0 3533
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
4 OK 0 255 640 978072
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\USERS01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 91
索引 0 39
其他 0 255
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
6 FAILED 0 4145 6400 1091128
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 2 2086
索引 0 0
其他 0 169
验证找到一个或多个损坏的块
有关详细资料, 请参阅跟踪文件 d:\u01\app\oracle\diag\rdbms\prod\prod\trace\pro
通道 ORA_DISK_1: 正在启动压缩的全部数据文件备份集
通道 ORA_DISK_1: 正在指定备份集内的数据文件
备份集内包括当前控制文件
通道 ORA_DISK_2: 备份集已完成, 经过时间:00:00:15
数据文件列表
=================
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
2 OK 0 19058 62785 1093071
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\SYSAUX01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 10266
索引 0 7049
其他 0 26347
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
3 OK 0 500 10880 1093073
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\UNDOTBS01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 0
索引 0 0
其他 0 10380
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
5 OK 0 1733 12804 1014837
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\EXAMPLE01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 4413
索引 0 1262
其他 0 5392
通道 ORA_DISK_2: 正在启动压缩的全部数据文件备份集
通道 ORA_DISK_2: 正在指定备份集内的数据文件
备份集内包括当前的 SPFILE
通道 ORA_DISK_1: 备份集已完成, 经过时间:00:00:00
控制文件和 SPFILE 的列表
===============================
文件类型 状态 失败的块 已检查的块
------------ ------ -------------- ---------------
控制文件 OK 0 594
通道 ORA_DISK_2: 备份集已完成, 经过时间:00:00:00
控制文件和 SPFILE 的列表
===============================
文件类型 状态 失败的块 已检查的块
------------ ------ -------------- ---------------
SPFILE OK 0 2
完成 backup 于 17-1月 -13
RMAN>
用RMAN的 backup validate database 命令检测完成后,如果有坏块,会将坏块写入到如下视图:
select * from v$database_block_corruption;
在警告日志里面也可以看到相关的块损坏:
ORACLE从9i开始支持用RMAN备份来修复数据块,最早没有提供这种功能,使用BBED来修复坏块。
BBED:block browse and edit.在这里不做论述。
利用RMAN备份恢复坏块:
再次检测发现只有一个错误了:
再来恢复一下:
恢复后再通过命令dbv来检测发现已经没有坏块了。
用analyze来分析也没有坏块了。
SQL> analyze table tt validate structure;
表已分析。
SQL>
对数据文件6再来进行一次备份前的校验
RMAN> backup validate datafile 6;
启动 backup 于 17-1月 -13
使用目标数据库控制文件替代恢复目录
分配的通道: ORA_DISK_1
通道 ORA_DISK_1: SID=40 设备类型=DISK
分配的通道: ORA_DISK_2
通道 ORA_DISK_2: SID=42 设备类型=DISK
通道 ORA_DISK_1: 正在启动压缩的全部数据文件备份集
通道 ORA_DISK_1: 正在指定备份集内的数据文件
输入数据文件: 文件号=00006 名称=D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF
通道 ORA_DISK_1: 备份集已完成, 经过时间:00:00:01
数据文件列表
=================
文件状态 标记为损坏 空块 已检查的块 高 SCN
---- ------ -------------- ------------ --------------- ----------
6 OK 0 4145 6400 1091128
文件名: D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF
块类型 失败的块 已处理的块
---------- -------------- ----------------
数据 0 2086
索引 0 0
其他 0 169
完成 backup 于 17-1月 -13
RMAN>
块恢复后再用backup validate database 检测一下,才会更新下面数据字典,发现已无坏块:
SQL> select * from v$database_block_corruption;
未选定行
SQL>
备份前可以先对数据库、数据文件、表空间做一个检查
RMAN> backup validate database;
RMAN> backup validate datafile 6;
RMAN> backup validate tablespace users;
查看恢复时会转储哪些备份
RMAN> restore preview database;
利用包DBMS_REPAIR来检测块损坏:
如果数据文件损坏,且没有备份,则可以用包dbms_repair来标记坏块,访问时跳过坏块,而不是报错。
首先要建立一张表用来存放损坏块的信息,利用包DBMS_REPAIR来创建
BEGIN
DBMS_REPAIR.ADMIN_TABLES(
table_name => 'REPAIR_TABLE',
TABLE_TYPE => DBMS_REPAIR.REPAIR_TABLE,
ACTION => DBMS_REPAIR.CREATE_ACTION,
TABLESPACE => 'USERS'); // 也可以放到SYSTEM 表空间及其他表空间里
END;
创建成功后我们看到没有任务数据
SQL> SELECT * FROM REPAIR_TABLE;
no rows selected
SQL>
执行过程,统计坏块,这里只是统计并不会标记这些块是坏的。如果一个块被标记为坏块。则
set serveroutput on
declare num_corrupt int;
begin
num_corrupt :=0;
dbms_repair.check_object(
schema_name => 'SYS',
object_name => 'TT',
repair_table_name => 'REPAIR_TABLE',
corrupt_count => num_corrupt);
dbms_output.put_line(num_corrupt);
END;
/
dbms_output.put_line(num_corrupt); // 用来输出坏块数据量
查看表REPAIR_TABLE,里面有了数据
SQL> SELECT count(*) FROM REPAIR_TABLE;
COUNT(*)
----------
5
SQL>
SQL> select OBJECT_ID,BLOCK_ID,OBJECT_NAME,REPAIR_DESCRIPTION from REPAIR_TABLE;
OBJECT_ID BLOCK_ID OBJECT_NAME REPAIR_DESCRIPTION
---------- ---------- ------------ ------------------------------
74569 781 TT mark block software corrupt // 可以看到是软损坏
74569 961 TT mark block software corrupt
74569 1326 TT mark block software corrupt
74569 1723 TT mark block software corrupt
74569 2197 TT mark block software corrupt
SQL>
将数据文件的坏块做标记,在访问时可以忽略坏块,以至于不报错,在没有标记坏块之前查询表会报错:
SQL> select count(*) from tt;
select count(*) from tt
*
ERROR at line 1:
ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 781) ORA-01110:
数据文件 6: 'D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF'
SQL>
用包DBMS_REPAIRE来标记坏块
SQL> exec dbms_repair.skip_corrupt_blocks(schema_name=>'SYS',OBJECT_NAME=>'TT');
PL/SQL procedure successfully completed.
SQL>
跳过去以后再查看就没问题了:
SQL> select count(*) from tt;
COUNT(*)
----------
144572
SQL>
执行上面的存储过程表其实就是更新了系统表DBA_TABLES
SQL> select skip_corrupt from dba_tables where table_name='TT';
SKIP_COR
--------
ENABLED
SQL>
再将标记过的坏块取消其标记,也就是让DBA_TABLES的SKIP_CORRUPT为DISABLE
SQL> exec dbms_repair.skip_corrupt_blocks(schema_name=>'SYS',object_name=>'TT',flags=>dbms_repair.noskip_flag);
PL/SQL procedure successfully completed.
SQL>
flags有两个值可选择:noskip_flag和skip_flag 前者表示2,后者表示1
如果设置为不路过坏块,则又会报错:
SQL> select count(*) from tt;
select count(*) from tt
*
ERROR at line 1:
ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 781) ORA-01110:
数据文件 6: 'D:\U01\APP\ORACLE\ORADATA\PROD\LXTBS01.DBF'
SQL>
我们可以单独为某个段进行检测,如果某个段特别大,而且跨了好几十个数据文件,那么以数据文件为单位进行检测则非常慢;
SEGMENT_ID 的三项内容表示下面的三个字段
SQL> select tablespace_id,header_file,header_block from sys_dba_segs where segment_name='TT';
TABLESPACE_ID HEADER_FILE HEADER_BLOCK
------------- ----------- ------------
7 6 130
SQL>
DB_BLOCK_CHECKING是一个参数
SQL> show parameter DB_BLOCK_CHECKING
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_checking string FALSE
SQL>
这个参数的作用就实时的验证数据块的完整性
后来因为这个参数的值太少就把FLASE改为了OFF