一、RMAN 恢复介绍

一般我们使用RMAN恢复,主要有如下两种方式:

  1. 全量恢复,可以恢复到指定时间点,也可以不指定时间,默认恢复到控制文件里存在的最后一个归档日志。

    场景: 一般情况下,我们不会直接恢复主库(也就是备份的这个库),而是恢复到一个临时库。当然,如果主库已经不可用,那就直接恢复到主库。这种情况一般都需要全量恢复。

  2. 恢复一个或多个归档日志文件。

    场景: 这种场景比较少,我遇到的就是ADG同步的备库同步出了问题,需要的归档日志异常(空间爆了导致同步失败),而主库可能已经把这个归档删了(但是最佳实践应该是主库设置好所有备库都应用日志后才删除)。

ORACLE的恢复,主要涉及以下三种文件:

  1. 控制文件:控制文件是ORACLE数据库的元数据信息,包括数据文件、表空间及其存储位置和大小,以及数据库的物理结构信息,如用户、角色、权限、表、视图、索引、存储过程、函数、触发器、序列、约束、注释等。
  2. 数据文件:数据文件是ORACLE数据库的数据文件,用于存储数据库中的数据。
  3. 归档日志:归档日志是ORACLE数据库的日志文件,用于记录数据库的修改操作,包括数据文件的创建、修改、删除等操作。

当我们需要进行ORACLE的恢复时,我们首先应该恢复一个控制文件,然后根据控制文件里存在的记录来恢复相应的时间点的数据备份和归档日志备份。所以,控制文件的选择是很关键的,如果选择错会导致无法找到备份记录导致恢复失败。

一般情况下,如何确定要恢复的控制文件呢?假设我们要恢复到2015年3月26日03这个时间点,那控制文件就要选择这个时间点之后的最近的一次备份记录。(能不能选更后面的?其实我觉得是可以的,但是我没测试)。

二、 RMAN 关键参数

这里只对涉及的关键参数进行简单介绍,完整参数介绍可以参考官方文档。

可以通过 show all; 命令查看rman设置的所有参数。
RMAN的保留策略配置: CONFIGURE RETENTION POLICY TO REDUNDANCY 1;

  1. REDUNDANCY number 基于冗余度策略,number默认是1,表示只保留1份最新的可用备份。如果有更新的备份,那之前的备份会被标记为“过时”,这种情况下只能恢复到最近一次备份的时间点。使用“REPORT OBSOLETE;”命令会清除过期的备份副本(只是针对rman,不会影响备份软件里的备份)。可以更改number大小来改变保留的备份数量。
  2. RECOVERY WINDOW NUMBER DAYS 基于恢复窗口策略,表示在恢复窗口内,可以恢复到任何时间点。如果恢复窗口内没有备份,那会报错。NUBER 一般设置为7天,表示7天内可以恢复到任何时间点。使用以下命令来设置: CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS; 建议生产采用该配置。
  3. 可将上面两种结合使用,哪个先到就以哪个为准: CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS AND REDUNDANCY 2;

控制文件自动备份:CONFIGURE CONTROLFILE AUTOBACKUP ON;
作用: 每次备份或数据库结构变更时,自动备份控制文件和SPFILE,确保恢复时能重建控制文件。因为我的环境使用NBU备份,脚本里是手动备份控制机的,所以不需要开启这个。

三、 RMAN 命令简介

进入rman之前,可以设置下显示的时间格式,这个在查看涉及到日期时比较有用:export NLS_DATE_FORMAT=‘yyyy-mm-dd hh24:mi:ss’

show all;  #查看当前参数设置
list backup of controlfile; ## 查看控制文件备份情况
REPORT SCHEMA;   ## 用于显示数据库的物理结构信息,包括数据文件、表空间及其存储位置和大小.
list backup of datafile + datafile_id ; ## 查看数据文件备份情况,后面要加上数据文件的ID。
list backup of database;   ## 查看数据库全量备份记录
list backup of database summary;  ## 查看数据库全量备份记录--简介
list backup of archivelog all;   ## 查看所有的归档日志备份记录
list backup of archivelog  sequence 393744 thread 1; ## 查看指定sequence的归档日志,注意thread 1表示归档日志线程,不加的话默认是1,如果是2就查不到,必须加 thread 2 来查。
list backup of archivelog from sequence 393744 until sequence 393748 thread 1;   ## 查看指定sequence范围的归档日志备份记录。 until可以不加,表示查到最新。
list backup of archivelog from time "to_date('2025-03-26 03:00:00','yyyy-mm-dd hh24:mi:ss')";  ## 查看指定时间范围的归档日志备份记录。后面不加until表示查到最新。也可以加: until  time "to_date('2025-03-26 03:30:00','yyyy-mm-dd hh24:mi:ss')";
list incarnation;  ## 查看当前数据库的化身。
reset database to incarnation 2;  ## 切换到指定化身

以上都是恢复时可能使用到的命令,当然还有很多其它命令,但由于我暂时没用到,这里就不再介绍。

四、 RMAN 恢复命令说明

涉及到两个重要的命令先介绍下:

  • restore database: 是“从备份复制文件”,将数据库还原到备份时的物理状态。说白了就是从备份软件里把文件恢复到本地。
  • recovery database: 是“修复事务一致性”,通过日志前滚/回滚使数据库逻辑一致。

1. 恢复控制文件

rman target /
RMAN>run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01';
restore controlfile from '/ora11_salesdb_cntrl_380191_1_1196743717';
}

2. 全量恢复到指定时间点

rman target / <<EOF
run {
set until time "to_date('03/26/2025 02','MM/DD/YYYY HH24')";
allocate channel ch00 type 'sbt_tape';
allocate channel ch01 type 'sbt_tape';
send 'nb_ora_client=timesdb01';
restore database;
switch datafile all;
recover database;
release channel ch00;
release channel ch01;
}
EOF 

3. 指定恢复某个归档日志

rman target / <<EOF
RUN {
allocate channel ch01 type 'sbt_tape';
send 'nb_ora_serv=nbusvr'; 
send 'nb_ora_client=timesdb01';
set archivelog destination to '/u01/app/oracle/flash_recovery_area/SALESUTF/archivelog';
restore archivelog from sequence 1152 thread 2;  ## 指定恢复某个归档日志
restore archivelog from sequence 379547 until sequence 379557 thread 2;  ## 指定恢复sequence范围内的归档
restore archivelog all;  ## 恢复所有归档
RELEASE CHANNEL ch01;
} 
EOF

五、实操

目标: 全量恢复一个最近的备份,然后再恢复指定的归档日志。

1. 准备好恢复环境

数据库版本跟主库(被备份的)版本要一致。安装了NBU客户端,配置好 /etc/hosts 文件。

[oracle@testora11 bin]$ ps -ef |grep netbackup
root       3019      1  0  2023 ?        00:17:55 /usr/openv/netbackup/bin/vnetd -standalone
root       3035      1  0  2023 ?        00:04:46 /usr/openv/netbackup/bin/bpcd -standalone
root       3089      1  0  2023 ?        00:37:27 /usr/openv/netbackup/bin/nbdisco
[oracle@testora11 ~]$ grep nbu /etc/hosts
101.11.9.100  nbusvr.test.cn  nbusvr
101.11.9.101  nbuapp01.test.cn  nbuapp01
101.11.9.102  nbuapp02.test.cn  nbuapp02

由于我这个环境他们之前已经使用过,所以我只是简单地检查了下。

2. 确认备份客户端名称

这个在NBU里面直接看备份策略里的设置就可以。

3. 列出可以恢复的备份文件:

[oracle@testora11 ~]$  /usr/openv/netbackup/bin/bplist -C timesdb01 -t 4 -b -l -R / | more
-rw-rw---- oracle    asmadmin    169607168 Mar 27 14:37 /c-367898914-20250327-0a
-rw-rw---- oracle    asmadmin    169607168 Mar 27 14:37 /ora11_ebsdb_cntrl_394560_1_1196865424
-rw-rw---- oracle    asmadmin    169607168 Mar 27 14:36 /c-367898914-20250327-09
-rw-rw---- oracle    asmadmin    23302144K Mar 27 14:34 /ora11_ebsdb_al_394558_1_1196865258
.........

由于我的环境是一体机,一台机上面存在多个实例,所以这里会显示很多,我们需要找到我们想要的那个库。这里我需要额外操作下:

[oracle@testora11 ~]$ /usr/openv/netbackup/bin/bplist -C timesdb01 -t 4 -b -l -R  > backup_list.txt
[oracle@testora11 ~]$ grep --binary-file=text sale backup_list.txt|more
-rw-rw---- oracle    asmadmin     73400320 Mar 26 06:22 /ora11_salesdb_cntrl_380196_1_1196749342
-rw-rw---- oracle    asmadmin    16346368K Mar 26 06:16 /ora11_salesdb_al_380195_1_1196749014
-rw-rw---- oracle    asmadmin    17786368K Mar 26 06:16 /ora11_salesdb_al_380194_1_1196749013
-rw-rw---- oracle    asmadmin    18766080K Mar 26 06:16 /ora11_salesdb_al_380193_1_1196749012
-rw-rw---- oracle    asmadmin    19601408K Mar 26 06:16 /ora11_salesdb_al_380192_1_1196749011
-rw-rw---- oracle    asmadmin     73400320 Mar 26 04:48 /ora11_salesdb_cntrl_380191_1_1196743717
........

这里我选择了 /ora11_salesdb_cntrl_380191_1_1196743717 这个控制文件进行恢复(因为我测试时这个是最新的)。

4. 切换到oracle用户,然后运行“/usr/openv/netbackup/bin/oracle_link”进行Oracle API接口库的连接。

5. 初始化配置文件

在$ORACLE_HOME/dbs目录下生成一个initsaleres.ora 配置文件(注意,这个配置文件命名应该是 init.ora),内容如下:

[oracle@testora11 dbs]$  cat initsaleres.ora
control_files = '/u01/app/data/control01.ctl'
compatible ='11.2.0.4'
db_name='salesutf'  ## 和原库一样名字
db_recovery_file_dest='$ORACLE_BASE/flash_recovery_area'
DB_RECOVERY_FILE_DEST_SIZE=2000G ## 闪回区大小,不能太小,太小会导致恢复过程中报错。这个应该是因为原库开启了闪回才需要。

6. 启动数据库到nomount模式

sqlplus / as sysdba <<EOF
startup nomount;
exit;
EOF 

7. 恢复控制文件

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01';
restore controlfile from '/ora11_salesdb_cntrl_380191_1_1196743717';
}

输出以下内容代表成功了:

output file name=/u01/app/data/control01.ctl
Finished restore at 21-MAR-25
released channel: ch00

8. 将数据库修改为mount状态

sqlplus / as sysdba <<EOF
alter database mount;
exit;
EOF

9. 列出要恢复的文件

RMAN> report schema;

这里会列出很多数据文件和临时表空间文件,这里后面要用到,因为太多,所以我们先导出来:

rman target / > /home/oracle/report.txt << EOF
report schema;
EOF

10. 生成恢复脚本

由于文件比较多,所以我编写了个脚本来快速生成:

[oracle@testora11 ~]$ cat init_script.sh 
echo "rman target / <<EOF
run {
set until time \"to_date('03/26/2025 00','MM/DD/YYYY HH24')\";
allocate channel ch00 type 'sbt_tape';
allocate channel ch01 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; " > recovery.sh
grep -E "DATAC1/salesutf/datafile" report.txt |while read line
do
   filename=`echo $line|awk -F"/" '{print $4}'`
   fileid=`echo $line|awk '{print $1}'`
   echo "set newname for datafile $fileid to '/u01/app/data/$filename';" >> recovery.sh
done
echo "restore database;
switch datafile all;
recover database;
release channel ch00;
release channel ch01;
}
EOF " >> recovery.sh

[oracle@testora11 ~]$ sh init_script.sh 

要留意”set newname for datafile $fileid to ‘/u01/app/data/$filename’;“,这个主要是因为原库是ASM的存储,而恢复库是本地存储,路径不一样,需要修改。另外,set until time 这个用来指定要恢复的时间点的,根据实际情况修改。

11. 执行恢复脚本

[oracle@testora11 ~]$ nohup sh recovery.sh > recovery.out &

12. 跟踪备份日志及结果

tail -f   recovery.out

13. 单独恢复某个归档文件

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; 
set archivelog destination to '/u01/app/oracle/flash_recovery_area/SALESUTF/archivelog';
restore archivelog sequence 393715 thread 1;
release channel ch00;
}

也顺利恢复了。注意,我这里只是把归档文件恢复了,并没有把归档文件注册到恢复库,所以恢复库里是没有归档日志里的数据的。

如果是要到注册数据库,需要从数据库当前最后一个归档日志的seq开始往后恢复。

14. 查看数据库当前应用的最后一个归档日志

查看上面的恢复日志 recovery.out,可以找到最后恢复的归档日志记录如下:

archived log file name=/u01/app/oracle/flash_recovery_area/SALESUTF/archivelog/2025_03_26/o1_mf_1_393654_my8340rl_.arc RECID=1851994 STAMP=1196806676
channel default: deleting archived log(s)
archived log file name=/u01/app/oracle/flash_recovery_area/SALESUTF/archivelog/2025_03_26/o1_mf_2_317579_my8340d3_.arc RECID=1851998 STAMP=1196806696

可以看到对应两个线程的最后一个归档日志的seq。 然后通过 rman 命令 list backup of archivelog from time “2025-03-25 14:00:00” 来查找这两个seq及其下面的seq如下:

  Thrd Seq     Low SCN    Low Time            Next SCN   Next Time
  ---- ------- ---------- ------------------- ---------- ---------
  1    393654  7446137842879 2025-03-25 13:47:39 7446140066142 2025-03-25 14:00:57
  1    393655  7446140066142 2025-03-25 14:00:57 7446141687331 2025-03-25 14:10:30
  
  2    317579  7446139272134 2025-03-25 13:55:58 7446140057344 2025-03-25 14:00:55
  2    317580  7446140057344 2025-03-25 14:00:55 7446140429296 2025-03-25 14:02:19

这里,我准备恢复393655 317580 这两个seq的归档日志。因为我是准备追加恢复的。

15. 恢复归档日志

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; 
set archivelog destination to '/u01/app/oracle/flash_recovery_area/SALESUTF/archivelog';
restore archivelog sequence 393655 thread 1;
restore archivelog sequence 317580 thread 2;
release channel ch00;
} 

显示正常恢复完成。查看归档日志文件:

[oracle@testora11 ~]$ ls -rlt /u01/app/oracle/flash_recovery_area/SALESUTF/archivelog
-rw-r----- 1 oracle oinstall  135337984 Mar 28 12:04 1_393655_1053606726.dbf
-rw-r----- 1 oracle oinstall 1834566656 Mar 28 12:06 2_317580_1053606726.dbf

这样就已经恢复出来文件了。如果是其它库需要,就可以直接复制过去使用。

16. 注册到数据库

如果是控制文件里没两个归档文件,那就需要注册到到控制文件。我这里测试的因为控制文件已有,所以就不需要注册了。

rman target /
CATALOG START WITH '/path/to/archivelog_directory/';

17. 追加恢复

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; 
RECOVER DATABASE UNTIL SEQUENCE 393655 THREAD 1;
RECOVER DATABASE UNTIL SEQUENCE 317580 THREAD 2;
release channel ch00;
} 

看恢复日志,会提示归档日志已经存在,然后就会恢复。如果不存在,这个命令是否也会自动去恢复归档文件呢,后面步骤测试下。

archived log for thread 2 with sequence 317580 is already on disk as file /u01/app/oracle/flash_recovery_area/SALESUTF/archivelog/2_317580_1053606726.dbf

第一个归档日志顺利追加到数据库,第二个归档日志报错了!

RMAN-06556: datafile 1 must be restored from backup older than SCN 7446140057344

很明显,犯了一个低级错误,那就是thread 1的下一个归档文件的SCN号比thread 2的下一个归档文件的SCN号高!! 所以,应该先恢复 THREAD 2 的SEQ 为317580的归档日志!

这里是个坑呀,就算先恢复了SCN号更高的归档日志也是会恢复成功的,恢复完成后就不能再恢复比这个SNC号低的归档文件了!没回退办法!只能重新再做一次权利恢复到旧版本,非常浪费时间。一定要注意避免这个错误!

建议用SCN号来恢复,选出SNC号最高的,例如我上面两个文件,SCN最高的是7446140066142,那就按下面来恢复:

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; 
RECOVER DATABASE UNTIL SCN 7446140066142 ;
release channel ch00;
} 

18. 追加恢复–直接恢复

不恢复归档文件,直接追加恢复,确实会自动恢复归档文件。

rman target /
run {
allocate channel ch00 type 'sbt_tape';
send 'nb_ora_client=timesdb01'; 
RECOVER DATABASE UNTIL SCN 7446141687331 ;
release channel ch00;
}