第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;
总结各块的功能:
- 200:第一级位图块
- 201:第二级位图块
- 202:表的段头
- 203:行数据块
- 204:暂无利用,高水位线以下
- 205:暂无利用,高水位线以下
- 206:暂无利用,高水位线以下
- 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功能:
- 快速检查数据块物理结构的完整性(不支持控制文件和日志文件)
工作模式:
- 文件模式
- 段模式(必须数据库打开状态)
# 文件模式
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页)