第15章 恢复受损的数据块

拥有健康的备份时遇到数据块坏块可以不还原整个数据文件,而使用rman的recover命令恢复受损数据块。

# 检查表的数据块分布情况   block_id表示起始块,blocks表示块数量
select (select name from v$datafile where file#=e.file_id) file_name,
file_id,block_id,blocks
from dba_extents e 
where owner='HR' and segment_name='EMPLOYEES';

# 转储5号文件200号块
alter system dump datafile 5 block 200;

# 获取5号文件200号块转储文件路径
select tracefile from v$process
where addr=(select paddr from v$session
where sid=sys_context('userenv','sid'));

# 检查数据块类型,确认是 frmt: 0x02 chkval: 0x9183 type: 0x20=FIRST LEVEL BITMAP BLOCK
grep -i 'type' /u01/app/oracle/diag/rdbms/icu/TESTDB/trace/TESTDB_ora_23531.trc

# 转储5号文件201号块
alter system dump datafile 5 block 201;

# 获取5号文件201号块转储文件路径
select tracefile from v$process
where addr=(select paddr from v$session
where sid=sys_context('userenv','sid'));

# 检查数据块类型,确认是 frmt: 0x02 chkval: 0xd54d type: 0x21=SECOND LEVEL BITMAP BLOCK
grep -i 'type' /u01/app/oracle/diag/rdbms/icu/TESTDB/trace/TESTDB_ora_23763.trc

# 检查5号文件202号块信息(EMPLOYEES的段头)
select header_file,header_block from dba_segments
where owner='HR' and segment_name='EMPLOYEES';

# 检查表EMPLOYEES的行数据保存在哪些数据块中         结果是203和207
select distinct dbms_rowid.rowid_block_number(rowid) as block# from hr.employees;

总结各块的功能:

  1. 200:第一级位图块
  2. 201:第二级位图块
  3. 202:表的段头
  4. 203:行数据块
  5. 204:暂无利用,高水位线以下
  6. 205:暂无利用,高水位线以下
  7. 206:暂无利用,高水位线以下
  8. 207:行数据块
# 目的:在操作系统层面使用dd命令将206号员工的工资由8300修改成830000

# 临时关闭db_block_checksum参数,默认情况下开启
show parameter db_block_checksum;
alter system set db_block_checksum='OFF';
show parameter db_block_checksum;

# 检查206号员工工资信息     Typ=2 Len=2: c2,54
select employee_id,salary,dump(salary,16) correct,dump(salary*100,16) falsify 
from hr.employees where employee_id=206;

# 检查206号员工的数据保存在哪个数据块中    
# 结果是203号,但真实物理块号是204号(oracle不使用文件的第1个块)
# rowid末尾三位为AAI,表示第9行(pri[8]),AAA为第1行
select rowid,dbms_rowid.rowid_block_number(rowid)
from hr.employees where employee_id=206;

# 转储5号文件203号块
alter system dump datafile 5 block 203;

# 获取转储文件路径
select tracefile from v$process
where addr=(select paddr from v$session
where sid=sys_context('userenv','sid'));

# 获取偏移量(pri[8])      offs=0x1d2a   对应的 10 进制数为 7466,表示偏移7466个字节
grep -i 'pri' /u01/app/oracle/diag/rdbms/icu/TESTDB/trace/TESTDB_ora_28221.trc

# 可推断206号员工数据处于5号文件的第(8192*203+7466)= 1670442字节
# 至(8192*204)= 1671168字节
# 使用hexdump命令确认,注意起始位置要减1        1671168-1670442=726
# 54c2对应 Typ=2 Len=2: c2,54,注意要颠倒顺序
hexdump -s 1670441 -n 726 /oradata/TESTDB/EXAMPLE01.DBF | egrep '54c2'

# 查询结果:0197dc9 0254 54c2 03ff 03c2 0306 02c2 2c0b 0b01
# 得到精确位置:0197dc9+3=1670601+3=1670604字节

# 生成二进制文件,内容只有一个字节:c3
# printf '\xc3' > source_test
# hexdump source_test

# 使用dd命令将第1670604字节c2修改为c3
# bs=1:每次读写1个字节,count=1:块数量,seek:跳过的字节,notrunc:不对文件进行截断收缩
echo -ne '\xc3' | dd of=/oradata/TESTDB/USERS01.DBF bs=1 count=1 seek=1670603 conv=notrunc


# 清理数据库缓存再检查206号员工工资信息,发现由 8300 > 830000
alter system flush buffer_cache;
select employee_id,salary,dump(salary,16) correct,dump(salary*100,16) falsify 
from hr.employees where employee_id=206;

# 打开db_block_checksum参数
alter system set db_block_checksum='TYPICAL';

# 清理数据库缓存再检查206号员工工资信息,发现包数据块错误,没有通过校验
alter system flush buffer_cache;
select employee_id,salary,dump(salary,16) correct,dump(salary*100,16) falsify 
from hr.employees where employee_id=206;

总结:任何情况下都不应关闭checksum功能。TYPICAL适合绝大多数环境

# 目的:在操作系统层面使用dd命令将1号员工的name由 毛翔 修改成 张三

# 临时关闭db_block_checksum参数,默认情况下开启
show parameter db_block_checksum;
alter system set db_block_checksum='OFF';
show parameter db_block_checksum;

# 检查1号员信息     Typ=1 Len=4: c3,ab,cf,e8
select id,name,dump(name,16) correct,dump('张三',16) falsify 
from hr.TEST01 where id=1;

# 检查1号员工的数据保存在哪个数据块中    
# 结果是525号,但真实物理块号是526号(oracle不使用文件的第1个块)
# rowid末尾三位为AAA,表示第1行(pri[0]),AAA为第1行
select rowid,dbms_rowid.rowid_block_number(rowid)
from hr.TEST01 where id=1;

# 转储4号文件525号块
alter system dump datafile 4 block 525;

# 获取转储文件路径
select tracefile from v$process
where addr=(select paddr from v$session
where sid=sys_context('userenv','sid'));

# 获取偏移量(pri[0])      offs=0x1f8d   对应的 10 进制数为 8125,表示偏移8125个字节
grep -i 'pri' /u01/app/oracle/diag/rdbms/icu/TESTDB/trace/TESTDB_ora_41005.trc

# 可推断1号员工数据处于4号文件的第(8192*525+8125)= 4308925 字节
# 至(8192*526)= 4308992字节
# 使用hexdump命令确认,注意起始位置要减1        4308992-4308925=67
# 54c2对应 Typ=1 Len=4: c3,ab,cf,e8,注意要颠倒顺序
hexdump -s 4308924 -n 67 /oradata/TESTDB/USERS01.DBF | egrep 'abc3'

# 查询结果:041bfec c004 cbee 2cc4 0200 c102 0402 abc3 e8cf
# 得到精确位置:041bfec+13=4308972+13=4308985字节
# 使用dd命令修改数据    abc3 e8cf 表示毛翔   c5d5 fdc8 表示张三
# bs=4:每次读写4个字节,count=1:块数量,seek:跳过的字节,notrunc:不对文件进行截断收缩
echo -ne '\xd5' | dd of=/oradata/TESTDB/USERS01.DBF bs=1 count=1 seek=4308984 conv=notrunc
echo -ne '\xc5' | dd of=/oradata/TESTDB/USERS01.DBF bs=1 count=1 seek=4308985 conv=notrunc
echo -ne '\xc8' | dd of=/oradata/TESTDB/USERS01.DBF bs=1 count=1 seek=4308986 conv=notrunc
echo -ne '\xfd' | dd of=/oradata/TESTDB/USERS01.DBF bs=1 count=1 seek=4308987 conv=notrunc
hexdump -s 4308924 -n 67 /oradata/TESTDB/USERS01.DBF 

# 清理数据库缓存再检查1号员工信息,发现name由 毛翔 > 张三
alter system flush buffer_cache;
select id,NAME from hr.TEST01 where id=1;

# 打开db_block_checksum参数
alter system set db_block_checksum='TYPICAL';

# 清理数据库缓存再检查1号员工信息,并不报错?只有修改数字类型的才报错吗?
alter system flush buffer_cache;
select id,NAME from hr.TEST01 where id=1;

dbv(278页)

dbv功能:

  • 快速检查数据块物理结构的完整性(不支持控制文件和日志文件)

工作模式:

  1. 文件模式
  2. 段模式(必须数据库打开状态)
# 文件模式
cd $ORACLE_HOME/bin
dbv FILE=/oradata/TESTDB/USERS01.DBF BLOCKSIZE=8192 FEEDBACK=50

# 段模式
# 获取表所在表空间编号、段头所在数据文件号、段头数据块号 : 6  5  202
select (select ts# from v$tablespace where name=s.tablespace_name) tablespace_name,header_file,header_block as segment_id
from dba_segments s
where owner='HR' and
segment_name='EMPLOYEES';

# 段模式
dbv USERID=xxzx/xxzx SEGMENT_ID=6.5.202 FEEDBACK=1

注意以下三个参数数据,不为0说明有坏块

Total Pages Failing (Seg) : 0

Total Pages Marked Corrupt : 0

Total Pages Influx : 0

analyze(281页)

功能:用于检查表和索引的逻辑完整性,配合dbv使用

# 执行analyze前序准备一张分析结果的表,默认名为INVALID_ROWS,通过脚本utlvalid.sql创建
sqlplus
@?/rdbms/admin/utlvalid.sql

# 查看表结构
desc INVALID_ROWS

# sqlplus下使用analyze分析表(加上cascade表示一并检查索引)
analyze table hr.employees validate structure cascade;

# 检查分析结果,有记录说明有问题,可通过head_rowid进一步查询到表的记录
select * from INVALID_ROWS;
SELECT * FROM hr.employees WHERE rowid='XXX';

dbms_hm.run_check存储过程

# 调用run_check过程检查 
begin
dbms_hm.run_check(
  check_name=>'Data Block Integrity Check',
  run_name=>'HM_CHECK',
  timeout=>null,
  input_params=>'BLC_DF_NUM=5;BLC_BL_NUM=203');
end;
/

# 查看检查结果
select dbms_hm.get_run_report('HM_CHECK') from dual;
select * from v$database_block_corruption;

rman的backup

RMAN的backup可以检查坏数据块,一旦读取到受损数据块,RMAN立刻停止工作,可以查看RMAN的追踪文件得到详细信息。

RMAN默认检查物理完整性和checksum,加上check logical额外检查逻辑完整性

backup check logical database;

数据块级恢复

前提:拥有健康的rman备份。

原理:利用备份还原健康的数据块,然后利用归档日志和在线日志将还原的数据块恢复,不支持增量备份和不完全恢复。

限制:不能恢复数据文件的第一个数据块,也就是文件头。

# 指定数据块恢复
rman target /
recover datafile 5 block 203;

# 批量恢复受损数据块
recover corruption list;

# 文件头损坏只能恢复整个数据文件
rm -rf /oradata/xx.dbf

run{
sql 'alter database datafile 5 offline';
restore datafile 5;
recover datafile 5;
sql 'alter database datafile 5 online';
}

数据块修复包dmbs_repair(295页)

fix_corrupt_blocks将受损快标记为已损坏

skip_corrupt_blocks忽略受损块,结果是访问受损块时不报错,但是受损数据块内的数据会丢失。

案例:从索引中抢救字段值(298页)


第1章 重做日志(Redo Log)

第2章 控制文件(Control File)

第3章 补充日志(Supplemental Logging)

第4章 恢复管理器(RMAN)

第5章 恢复编录(Recovery Catalog)

第6章 数据泵(Data Pump)

第7章 参数文件的备份与还原

第8章 控制文件的备份与恢复

第9章 关键数据文件备份与恢复

第10章 普通数据文件备份与恢复

第11章 只读数据文件备份与恢复

第12章 不完全数据库恢复

第13章 无备份情况下的恢复

第14章 闪回技术(Flashback)

第15章 恢复受损的数据块