书名 | 临危不惧:Oracle 11g数据库恢复技术 |
作者 | 包光磊 |
简介 | 《临危不惧:Oracle 11g数据库恢复技术》分为三大部分:恢复的原理、恢复的工具、恢复的具体步骤与实战。第一部分能够让读者领略恢复操作的本质,是其他部分的理论基础,包括“重做日志”、“控制文件”、“补充日志”;第二部分详细地介绍了备份/恢复数据库的工具,包括恢复管理器、恢复编录和数据泵;第三部分以各种数据库损毁场景为例,每一章解决一组特定的问题,其中:第7~11章讨论各种文件(控制文件、数据文件等)遭到不同程度的破坏后,数据库的行为特征及如何将其恢复;第12章探讨如何将数据库恢复至以前的时间点;第13章讨论如何处理各种数据库文件在没有备份时受损的情况;第14章全面介绍了用于恢复人为错误的各种闪回技术:第15章介绍将恢复的单位缩小,精确到数据块级别,以提高恢复效率;第16章介绍一种非常规恢复的方法一挖掘日志。 |
第1章 重做日志(Redo Log)
Oracle数据库三大核心文件:数据文件、重做日志(在线和归档)、控制文件
5个关键概念
- RBA(Redo Byte Address,重做字节地址)
- SCN(System Change Number,系统变更号)
- DBA(相对数据块地址)
- 数据块版本号(SCN+SEQ)
- Checkpoint(检查点)
重做记录
对数据库任何形式的更改,都会在更改操作之前产生一条重做记录。
重做记录包含一个或多个变更矢量(Change Vector),变更矢量保证了数据块在修改前后的一致性,重做记录保证了数据库在修改前后的一致性。
- SCN:Oracle表示时间流逝的一种方式,一个数据库只有一个全局的SCN产生器,数据库中任何变更(包括访问控制文件),SCN都会增加,即使没有任何变更,每3秒也至少加1。数据块头部信息记录了SCN,可能存在多条重做记录的SCN相同,此时用SUBSCN来区分,SUBSCN又称SEQ,SCN+SEQ就是数据块版本号,SCN占6字节,SEQ占1字节。
- SCN理论最大值为0xffff.ffffffff,即1612,用10进制表示为281474976710655,用尽后会循环到最小值。
SCN+SEQ信息同时出现在重做日志和数据文件中。如"SCN:0x0000.000FD122 SUBSCN:1"。
#查询v$database等同于访问控制文件,每次查询都会生成一个新的SCN
select current_scn from v$database;
#如果想查询当前的SCN而不是产生一个新的SCN,应该使用以下查询
select dbms_flashback.get_system_change_number from dual;
- RBA:由4部分组成:日志线程号、日志序列号、日志文件块编号、日志文件块字节偏移量,占10字节。
如"Thread:1 RBA:0x000025.00000003.0010"
表示在1号线程37号序列号重做日志文件的第3个块中的第16个字节处。
- BASE64转换表
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
sqlplus sys/oracle@testdb as sysdba
set line 500;
select rowid,salary,first_name,last_name from hr.employees where employee_id=100;
ROWID SALARY FIRST_NAME LAST_NAME
----------------- ---------- ---------------------------- -------------------------
AAAXZ5AAEAAAAD7AAA 24000 Steven King
rowid(AAAXZ5AAEAAAAD7AAA)查询BASE转换表手工解析:
前6位AAAXZ5代表 OBJECT_ID 23*642+25*641+57*640=95865
接下来3位AAE代表 数据文件号 4*640=4
接下来6位AAAAD7代表 数据块编号 3*641+59*640=251
接下来3位AAA代表 行编号 0*640=0
SELECT
DBMS_ROWID.ROWID_OBJECT(ROWID) AS object_id,
DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) AS file_number,
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) AS block_number,
DBMS_ROWID.ROWID_ROW_NUMBER(ROWID) AS row_number
FROM
hr.employees where employee_id=100;
OBJECT_ID FILE_NUMBER BLOCK_NUMBER ROW_NUMBER
---------- ----------- ------------ ----------
95865 4 251 0
- 转储重做日志文件信息
#启用最小补充日志
alter database add supplemental log data;
#切换redo日志
alter system switch logfile;
#查询redo日志信息
select lg.group#,lg.sequence#,lgf.member,lg.status
from v$log lg,v$logfile lgf where lg.group#=lgf.group#;
#查询数据行信息 file_id,block_id,rowid
select dbms_rowid.rowid_relative_fno(rowid) file_id,
dbms_rowid.rowid_block_number(rowid) block_id,rowid,
employees.salary from hr.employees where employee_id=100;
FILE_ID BLOCK_ID ROWID SALARY
---------- ---------- ------------------ ----------
4 251 AAAXZ5AAEAAAAD7AAA 24000
#执行update更新数据
update hr.employees set salary=salary*1.2 where employee_id=100;
#转储重做日志文件
alter system dump logfile '/u01/data/HISDB/onlinelog/o1_mf_11_m3p4mk7m_.log' dba min 4 251 dba max 4 251;
#ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME REDOHDR LEVEL 10';
#查看转储文件路径
select value from v$diag_info where name='Default Trace File';
update操作产生的变更矢量(redo日志中)
- CHANGE#1 AFN:3,DBA:0x00c00080,记录修改前的SCN+SEQ
- CHANGE#2 AFN:3,DBA:0x00c0172e,在3号文件(undo)的5934号数据块中记录修改前的值:第1行第8个字段,修改前的值为c30329
- CHANGE#3 AFN:4,DBA:0x010000fb,记录修改4号文件251号数据块第1行第8个字段,修改值为c30359
- CHANGE#4 没有更改意图
update的目的是修改4号文件251号数据块(CHANGE #3),但是必须先在3号文件128号块注册事务表(CHANGE #1),并在事务表中的3号文件5934号数据块中保存撤销数据(CHANGE #2)保证事务可以被回滚
# CHANGE#1 12583040 为DBA 0x00c00080 转换成10进制的数值
select dbms_utility.data_block_address_file(12583040) as rfile,
dbms_utility.data_block_address_block(12583040) as block from dual;
RFILE BLOCK
---------- ----------
3 128
# CHANGE#2 12588846 为DBA 0x00c0172e 转换成10进制的数值
select dbms_utility.data_block_address_file(12588846) as rfile,
dbms_utility.data_block_address_block(12588846) as block from dual;
RFILE BLOCK
---------- ----------
3 5934
# CHANGE#2 16777467 为DBA 0x010000fb 转换成10进制的数值
select dbms_utility.data_block_address_file(16777467) as rfile,
dbms_utility.data_block_address_block(16777467) as block from dual;
RFILE BLOCK
---------- ----------
4 251
select file#, name from v$datafile;
FILE# NAME
---------- -------------------------------------------
1 /u01/data/hisdb/system01.dbf
2 /u01/data/hisdb/sysaux01.dbf
3 /u01/data/hisdb/undotbs01.dbf
4 /u01/data/hisdb/users01.dbf
SELECT DUMP(24000,16) FROM DUAL;
DUMP(24000,16)
----------------------------------------
Typ=2 Len=3: c3,3,29
SELECT DUMP(28800,16) FROM DUAL;
DUMP(28800,16)
----------------------------------------
Typ=2 Len=3: c3,3,59
show parameter log_buffer;
LGWR将缓冲区数据写入在线重做日志的条件:
- 每隔3秒
- 日志缓冲区数据超过1MB
- 日志缓冲区数据达到1/3容量
- 执行commit
- 在DBWn进程中将脏数据块写入数据文件之前
在线重做日志
每组在线重做日志建议至少配置2个成员,互为镜像,其中1个成员文件故障并不影响oracle的启动和正常运行,但是在日志中会有告警记录。
trace路径:$ORACLE_BASE/diag/rdbms/hisdb/hisdb/trace
假设一个数据库操作产生的重做记录为R,脏数据块(存在 Database Buffer Cache 中)为D,在LGWR没有把R写入在线重做日志文件的情况下,Oracle是不允许DBWn将D写入数据文件的。所以在数据库打开的情况下,数据文件永远比重做日志文件旧。
检查点
检查点的目的是将检查点目标(某条重做记录及其头部的RBA和SCN)写入数据文件和控制文件。
参与检查点的进程包括LGWR、DBWn、CKPT
检查点分为完全检查点和增量检查点
完全检查点
- 在日志缓冲区中提取最新的重做记录的RBA和SCN作为检查点目标
- LGWR清空日志缓冲,将其写入在线日志
- DBWn将检查点目标(RBA和SCN)及之前的所有脏数据按RBA顺序写入数据文件
- CKPT将检查点目标(RBA和SCN)写入数据文件头部和控制文件
完全检查点发生时机
- shutdown immediate (高优先级,立即完成)
- alter system checkpoint (高优先级,立即完成)
- alter system switch logfile (低优先级,延迟几分钟完成,查看alert日志)
Fri May 10 04:24:48 2024
Beginning log switch checkpoint up to RBA [0x1b.2.10], SCN: 1030971
Thread 1 advanced to log sequence 27 (LGWR switch)
Current log# 3 seq# 27 mem# 0: /data/itpuxdb/redo03.log
Fri May 10 04:27:23 2024
Completed checkpoint up to RBA [0x1b.2.10], SCN: 1030971
- 维护表空间操作时 (不完整,仅将特定表空间的脏数据块写回数据文件)
#修改初始化参数
alter system set log_checkpoints_to_alert=TRUE;
#创建完全检查点
alter system checkpoint;
Fri May 10 04:18:07 2024
Beginning global checkpoint up to RBA [0x19.22b.10], SCN: 1030819
Completed checkpoint up to RBA [0x19.22b.10], SCN: 1030819
#表示25号重做日志555号日志块中的第16字节,SCN为1030819
#查询最近一次已完成的完全检查点SCN
select file#,checkpoint_change# from v$datafile_header;
#查询数据文件的检查点SCN
select file#,name,checkpoint_change# from v$datafile;
增量检查点(由Oracle自动控制)
- 确定一条非最新重做记录的RBA和SCN作为检查点目标(小于在线日志大小总和与最大在线日志大小差的0.9倍,假设有3组重做日志,每组大小为1G,则计算公式为(3-1)*0.9=1.8G,增量检查点的RBA距离当前最新的RBA的“距离”不能大于1.8G)
- LGWR清空日志缓冲,将其写入在线日志
- DBWn将检查点目标(RBA和SCN)及之前的所有脏数据按RBA顺序写入数据文件
- CKPT将检查点目标(RBA和SCN)写入控制文件
增量检查点存在的意义:减少发生完全检查点时DBWn的工作负担,提高实例恢复的速度
实例恢复
数据库正常关闭时会发起完全检查点,此时停止任何更新,LGWR将日志缓冲区数据写入在线重做日志,DBWn将脏数据块写入数据文件,数据文件与在线重做日志文件正真同步。每次打开数据库时Oracle都会执行该检查。
数据库异常关闭时,如断电、shutdown abort,Oracle没来得及完成完全检查点,数据文件比在线重做日志文件要旧,下一次打开数据库时,同步测试无法通过,此时如果控制文件、数据文件、在线重做日志文件都没有损坏,Oracle会自动发起实例恢复的操作同步数据文件,并且在告警日志中留下以下痕迹:
ALTER DATABASE OPEN
Beginning crash recovery of 1 threads
parallel recovery started with 3 processes
Started redo scan
Completed redo scan
read 62 KB redo, 39 data blocks need recovery
Started redo application at
Thread 1: logseq 31, block 3
Recovery of Online Redo Log: Thread 1 Group 1 Seq 31 Reading mem 0
Mem# 0: /data/itpuxdb/redo01.log
Recovery of Online Redo Log: Thread 1 Group 2 Seq 32 Reading mem 0
Mem# 0: /data/itpuxdb/redo02A.log
Mem# 1: /data/itpuxdb/redo02B.log
Recovery of Online Redo Log: Thread 1 Group 3 Seq 33 Reading mem 0
Mem# 0: /data/itpuxdb/redo03.log
Completed redo application of 0.04MB
Completed crash recovery at
Thread 1: logseq 33, block 2, scn 1074188
39 data blocks read, 39 data blocks written, 62 redo k-bytes read
数据库异常关闭后再启动恢复时步骤:
- startup
- 打开参数文件
- 打开控制文件
- 检查数据文件和日志文件是否同步
- 前滚重做日志中所有记录
- 数据库即可打开接收客户端请求
- 回滚没有提交的事务
两次读取法(仅在实例恢复时使用):书本第18页
select lg.group#,lg.sequence#,lg.status,lf.member
,to_char(first_time,'YYYY-MM-DD HH24:MM:SS') AS first_time
from v$log lg inner join v$logfile lf on lg.group#=lf.group#;
- CURRENT 表示当前正在使用的,是实例恢复需要的
- ACTIVE 最近一次完全检查点的RBA、SCN小于该日志中最后一条重做记录的RBA和SCN,是实例恢复需要的
- INACTIVE 最近一次完全检查点的RBA、SCN大于该日志中最后一条重做记录的RBA和SCN,是实例恢复不需要的
如果LGWR的下一个日志状态是ACTIVE,则LGWR会被挂起,Oracle自动发起一个高优先级的完全检查点,完成后日志状态会从ACTIVE变成INACTIVE,此时LGWR才能覆盖写入该日志。
归档日志
归档日志是在线重做日志的备份,由ARCn在LGWR覆盖在线重做日志之前复制而来,在数据库启动时不会被打开,没有维护的情况下数量会不断增加,主要作用是支持介质恢复和RMAN在线备份。
archive log list;
select log_mode from v$database;
#开启归档
alter database archivelog;
#查询归档信息
select sequence#,first_change#,next_change#,name from v$archived_log;
#设置归档路径为/data/arch
alter system set log_archive_dest_1='location=/data/arch';
#设置归档文件格式 (保存在快速恢复区时有另外的命名规范,不受此参数控制)
alter system set log_archive_format='arc_hisdb_%t_%s_%r' scope=spfile;
归档日志路径(优先级从上至下,上面的缺省则使用下面的)
- log_archive_dest_N参数
- db_recovery_file_dest参数指向的快速恢复区
- $ORACLE_HOME/dbs
修改初始化参数log_archive_max_processes可以启动多个ARCn进程(最多30)
介质恢复
与实例恢复的区别是实例恢复没有文件损坏,知识文件不同步(数据文件比日志文件旧),而介质恢复是有文件损坏,将其还原、恢复的过程。
recover automatic datafile 1;
#第26页
★日志转储
#转储指定数据块修改的重做记录(4号数据文件中251-260号数据块)
alter system dump logfile '/extend/oradata/hisdb/redo01A.log' dba min 4 251 dba max 4 260;
#转储指定RBA(22号日志中10-20号块)日志文件的块大小和数据文件的块大小不一样
alter system dump logfile '/extend/oradata/hisdb/redo01A.log' rba min 22 10 rba max 22 20;
#转储指定SCN号(123456)的重做记录
alter system dump logfile '/extend/oradata/hisdb/redo01A.log' scn min 123456 scn max 123456;
#转储日志文件头部信息
alter session set events 'immediate trace name redohdr level 10';
#转储整个日志文件
alter system dump logfile '/extend/oradata/hisdb/redo01A.log'
#查看转储文件路径
select value from v$diag_info where name='Default Trace File';