1. 为什么RMAN远不止一条backup命令
很多人第一次接触RMAN,脑子里留下的印象就是一条backup database,跑完看到一片"Finished"就以为万事大吉。真到了要恢复的那天,才发现自己连归档日志放哪、控制文件有没有自动备份、通道怎么配都说不清楚。我见过太多这样的场景:备份脚本天天在跑,日志里全是成功,结果真出事的时候,恢复出来的库缺了一段归档,或者干脆连备份集都认不出来。
RMAN这套东西,表面上是Oracle自带的一个备份工具,实际上它是一整套围绕备份集(backup set)、镜像副本(image copy)、恢复目录(recovery catalog)、**通道(channel)**构建起来的体系。你只用了它10%的能力,剩下90%在关键时刻才是救命的。这篇内容就是把我这些年踩过的坑、补过的课整理出来,重点讲那些文档里一笔带过、但实操中一定会遇到的知识点。
适合谁看?如果你已经会写基本的RMAN备份脚本,但对其中的参数、保留策略、增量机制、恢复流程的细节还模棱两可,那这篇就是给你补的。如果你是完全的新手,建议先把backup和restore的基本语法过一遍再回来,不然有些地方会跟不上。下面所有内容都基于常见的企业级实践,具体环境需要你根据自己的版本和架构做调整。
2. 备份集、镜像副本与保留策略的底层逻辑
2.1 备份集和镜像副本到底该选哪个
RMAN输出备份有两种形态,这个区分是理解后面一切的基础。
**备份集(backup set)**是RMAN的默认输出,它把数据文件里用过的数据块抽出来,压成一个或多个专有格式的文件。特点是体积小、支持压缩、支持增量,但你不能直接用操作系统命令去读它,必须通过RMAN来restore。**镜像副本(image copy)**则是把数据文件原样复制一份,和操作系统的cp结果几乎一样,好处是可以直接拿来用,恢复时能省掉restore这一步,直接switch过去就行,但占空间大、不支持增量。
我一般的判断标准是这样的:
- 磁盘空间紧张、备份窗口短、需要长期保留多份 → 用备份集
- 对恢复时间(RTO)要求极高、需要快速回切 → 关键数据文件做镜像副本
- 做增量备份 → 只能用备份集
这里有个容易被忽略的点:备份集本身也可以被增量更新。也就是说,你可以先做一个全量备份集,之后每天做增量备份集,RMAN会把增量合并进去,这就是后面要讲的增量更新备份(incrementally updated backup)。这个机制能让你既享受备份集的省空间,又享受接近镜像副本的恢复速度。
2.2 保留策略:别让"冗余"和"恢复窗口"打架
保留策略决定了RMAN认为哪些备份是"过期"的,可以删。有两种配置方式:
- 冗余度(redundancy):保留N份最近的备份。比如
CONFIGURE RETENTION POLICY TO REDUNDANCY 2,就是每个数据文件至少保留2份备份。 - 恢复窗口(recovery window):保证能恢复到过去N天内的任意时间点。比如
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS。
这两个不能同时生效,你配了后者,前者就失效。很多人图省事配了冗余度,结果发现要恢复到上周某个时间点时,备份虽然有两份,但归档日志早被删了,照样恢复不了。恢复窗口才是真正对应业务RPO的指标,如果你的业务要求"能恢复到7天内任意时刻",那就必须用恢复窗口,并且确保归档日志的保留也覆盖这个窗口。
注意:恢复窗口策略下,RMAN会保留"能支撑恢复到窗口起点"所需的最少备份。这意味着它可能只留一份全备加一串归档,而不是留很多份全备。如果你还想要多份全备做冗余,得用
KEEP或者手动管理。
2.3 归档日志的删除策略是个大坑
归档日志的清理是RMAN里最容易出事的地方。默认情况下,RMAN不会自动删归档,除非你显式配置。常见的做法是配一条:
CONFIGURE ARCHIVELOG DELETION POLICY TO BACKED UP 2 TIMES TO DISK;意思是归档日志至少被备份到磁盘2次之后才允许删除。但这里有个陷阱:如果你只备份到磁盘,没备份到磁带或其他介质,这条策略可能让归档永远删不掉,或者反过来,如果你配了APPLIED ON STANDBY(针对有备库的场景),主库上的归档可能被过早删除。
我的经验是,归档删除策略一定要和你的备份策略、Data Guard配置一起考虑。如果只有单机,用BACKED UP 1 TIMES TO DISK配合定期全备比较稳妥;如果有备库,要确保归档在备库应用之后再删。删归档之前,永远先跑一遍CROSSCHECK ARCHIVELOG ALL确认状态,别让RMAN以为某个归档还在、实际文件已经没了。
3. 通道配置与并行度:决定备份快慢的关键
3.1 通道到底在干什么
通道(channel)是RMAN和存储之间的一个连接。你配几个通道,RMAN就能并行读几个数据文件、并行写几路输出。很多人备份慢,第一反应是磁盘不行,其实往往是通道没配对。
通道分两类:磁盘通道(DISK)和磁带通道(S3/TAPE)。磁盘通道的并行度受限于你的磁盘IO能力,一般配2到4个就够了,配太多反而因为争抢IO导致整体变慢。磁带通道则要看磁带机的数量,通常一个磁带机配一个通道。
配置方式有两种,一种是持久化配置:
CONFIGURE DEVICE TYPE DISK PARALLELISM 4; CONFIGURE CHANNEL DEVICE TYPE DISK FORMAT '/backup/rman/%U';另一种是在命令里临时指定:
RUN { ALLOCATE CHANNEL c1 DEVICE TYPE DISK FORMAT '/backup/rman/df_%U'; ALLOCATE CHANNEL c2 DEVICE TYPE DISK FORMAT '/backup/rman/df_%U'; BACKUP DATABASE; }临时指定的好处是灵活,比如大表空间用单独通道、小表空间合并备份。但要注意,通道数乘以每个通道的读速率不能超过存储的极限,否则就是自己跟自己抢带宽。
3.2 并行度怎么算才合理
这里给一个我常用的估算方法。假设你的存储能提供500MB/s的持续读带宽,单个通道大概能跑到80到120MB/s(取决于块大小和文件分布),那么配4到5个通道比较合适。如果你配了8个通道,每个只能跑到60MB/s,总带宽还是500MB/s左右,但CPU和内存开销上去了,得不偿失。
还有一个细节:大文件和小文件混在一起时,通道分配不均。RMAN默认按数据文件分配通道,如果某个数据文件特别大,它占用的那个通道就会成为瓶颈。解决办法是用SECTION SIZE把大文件切成多段并行备份:
BACKUP DATABASE SECTION SIZE 2G;这样每个数据文件会被切成2G的段,多个通道可以同时处理同一个文件的不同段,大文件不再是瓶颈。
3.3 备份格式里的%U和%u别搞混
FORMAT参数里的占位符很容易记错:
| 占位符 | 含义 |
|---|---|
| %U | 唯一文件名,包含%u_%p_%c的组合,最常用 |
| %u | 8位随机唯一标识 |
| %p | 备份片编号,从1开始 |
| %c | 备份集内的副本编号 |
| %d | 数据库名 |
| %T | 时间戳,格式YYYYMMDD |
| %t | 时间戳,格式YYYYMMDDHHMMSS |
我一般用/backup/rman/%d_%T_%U.bkp这种格式,既有库名又有日期,方便人工排查。千万别用固定文件名,否则第二次备份会直接覆盖第一次,RMAN虽然会报错,但如果你用了FORMAT '/backup/rman/backup.bkp'这种,多个备份片会互相覆盖,后果很严重。
4. 增量备份的三种级别与实战选择
4.1 差异增量和累积增量的区别
RMAN的增量备份分两种:
- 差异增量(differential):备份自上次同级或更低级备份以来变化的数据块。这是默认级别。
- 累积增量(cumulative):备份自上次更低级备份以来变化的数据块。
举个例子,假设你周日做0级(全量),周一周二做1级差异增量,周三做1级累积增量:
- 周一的差异增量:备份周日0级之后变化的块
- 周二的差异增量:备份周一1级之后变化的块
- 周三的累积增量:备份周日0级之后所有变化的块(包含周一周二的变化)
差异增量的好处是每次备份小、快;坏处是恢复时要依次应用所有增量。累积增量的好处是恢复时只需要0级加最近一次累积,坏处是每次备份越来越大。
4.2 块变更跟踪:让增量备份快10倍
默认情况下,RMAN做增量备份时要扫描整个数据文件,找出哪些块变了。这个扫描过程在大库上非常慢。**块变更跟踪(Block Change Tracking,BCT)**就是解决这个问题的:Oracle在后台维护一个位图文件,记录每个块自上次备份后是否变化,增量备份时直接读位图,不用扫全文件。
开启方法:
ALTER DATABASE ENABLE BLOCK CHANGE TRACKING USING FILE '/oradata/bct/chg_track.ctf';开启后,第一次增量备份还是全扫,之后就会快很多。我实测过一个800G的库,没开BCT时1级增量要40分钟,开了之后只要4分钟。BCT文件建议放在独立的磁盘上,不要和数据文件放一起,否则会争抢IO。
注意:BCT文件本身也需要空间,大约是数据文件总大小的1/30000。如果BCT文件损坏或丢失,增量备份会自动退化成全扫,不会报错,但速度会掉下来。定期检查
V$BLOCK_CHANGE_TRACKING视图确认状态。
4.3 增量更新备份:兼顾空间和恢复速度
增量更新备份(incrementally updated backup)是我个人最推荐的策略之一。做法是:
- 先做一个0级镜像副本(image copy)
- 之后每天做1级增量备份,但用
BACKUP INCREMENTAL LEVEL 1 FOR RECOVER OF COPY语法,让RMAN把增量直接合并到镜像副本里
这样镜像副本始终保持最新,恢复时直接switch过去,不用restore。命令大概长这样:
RUN { RECOVER COPY OF DATABASE WITH TAG 'incr_update'; BACKUP INCREMENTAL LEVEL 1 FOR RECOVER OF COPY WITH TAG 'incr_update' DATABASE; }第一句是把之前的增量合并进副本,第二句是做新的增量。这个策略的妙处在于:你每天只备份变化的部分,但手里始终有一份最新的全量镜像。恢复时间从"restore全备+应用归档"变成"switch副本+应用少量归档",RTO能缩短一个数量级。
5. 恢复场景实战:从控制文件丢失到不完全恢复
5.1 控制文件丢失怎么救
控制文件是RMAN的"地图",丢了它,RMAN就不知道备份在哪、归档在哪。但控制文件本身是可以从备份里恢复的,前提是你开了控制文件自动备份:
CONFIGURE CONTROLFILE AUTOBACKUP ON; CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '/backup/rman/cf_%F';开了之后,每次备份数据文件或做结构变更,RMAN都会自动备份控制文件和参数文件。恢复时,即使没有恢复目录,RMAN也能通过%F格式找到自动备份:
STARTUP NOMOUNT; SET DBID 1234567890; RESTORE CONTROLFILE FROM AUTOBACKUP; ALTER DATABASE MOUNT; RESTORE DATABASE; RECOVER DATABASE;这里SET DBID是关键,因为控制文件丢了之后,RMAN不知道你要恢复哪个库,必须手动告诉它DBID。DBID可以从之前的备份日志里找,或者从自动备份文件名里推断。
5.2 不完全恢复的三种类型
不完全恢复是指恢复到某个时间点、SCN或日志序列号,而不是恢复到最新状态。三种类型:
- 基于时间(UNTIL TIME):恢复到某个时间点,比如
RECOVER DATABASE UNTIL TIME "TO_DATE('2024-01-15 10:00:00','YYYY-MM-DD HH24:MI:SS')" - 基于SCN(UNTIL SCN):恢复到某个SCN
- 基于日志序列(UNTIL SEQUENCE):恢复到某个日志序列号之前
做不完全恢复前,一定要先做一次全备,因为不完全恢复会重置日志序列,之后原来的备份可能就用不了了。恢复完成后要用RESETLOGS打开数据库:
ALTER DATABASE OPEN RESETLOGS;RESETLOGS会重置在线日志,生成新的日志序列。这一步之后,之前的归档日志就不能再用于恢复了,所以做之前想清楚。
5.3 表空间时间点恢复:不用恢复整个库
有时候只是某张表被误删了,或者某个表空间的数据被改错了,没必要恢复整个库。**表空间时间点恢复(TSPITR)**可以只把某个表空间恢复到过去某个时间点,其他表空间不受影响。
做法是:RMAN会自动创建一个辅助实例,把表空间恢复到指定时间点,然后导出数据再导入回原库。命令大概是这样:
RECOVER TABLESPACE users UNTIL TIME "TO_DATE('2024-01-15 09:00:00','YYYY-MM-DD HH24:MI:SS')" AUXILIARY DESTINATION '/tmp/aux';TSPITR的坑在于:辅助实例需要足够的空间,而且如果表空间和其他表空间有外键关联,恢复后可能不一致。所以做完之后要检查数据完整性。另外,TSPITR期间原库可以保持打开,但被恢复的表空间在恢复过程中不可用。
6. 常见问题排查与避坑清单
6.1 备份慢、恢复慢的排查思路
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 备份慢 | 通道数不足 | 检查V$RMAN_BACKUP_JOB_DETAILS的并行度 |
| 备份慢 | 未开BCT | 查V$BLOCK_CHANGE_TRACKING |
| 备份慢 | 备份到同一块盘 | 确认备份路径和数据文件不在同一物理盘 |
| 恢复慢 | 用了备份集而非镜像副本 | 考虑增量更新备份策略 |
| 恢复慢 | 归档应用慢 | 检查归档日志是否在慢速存储上 |
| 报错RMAN-06023 | 找不到备份片 | CROSSCHECK BACKUP后LIST EXPIRED |
6.2 那些文档不会告诉你的坑
坑一:DELETE OBSOLETE删太快。如果你配了恢复窗口7天,但某天做了一次全备后立刻跑DELETE OBSOLETE,RMAN可能会把支撑窗口起点的旧备份删掉,导致窗口实际缩短。建议删之前先REPORT OBSOLETE看看要删什么。
坑二:归档日志被操作系统脚本删了。有些DBA图省事,写个crontab直接rm归档目录,结果RMAN的元数据里还记着这些归档,恢复时报RMAN-06059。正确做法是用DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7'让RMAN自己删。
坑三:BACKUP DATABASE PLUS ARCHIVELOG的顺序。这条命令会先备份归档、再备份数据文件、再备份一次归档。中间那次归档备份是为了保证一致性。但如果你归档量很大,这个命令会跑很久。可以拆成两步:先BACKUP ARCHIVELOG ALL,再BACKUP DATABASE。
坑四:恢复目录(catalog)没同步。如果你用了恢复目录,但某次备份没注册进去,恢复时catalog里找不到。定期跑RESYNC CATALOG,或者干脆用控制文件作为唯一元数据源(不用catalog),省去同步的麻烦。
坑五:VALIDATE和RESTORE VALIDATE的区别。VALIDATE只检查备份集是否可读,不实际恢复;RESTORE VALIDATE会模拟恢复过程,检查更彻底。定期做RESTORE DATABASE VALIDATE能提前发现坏块,别等真恢复时才发现备份是坏的。
6.3 一个实用的备份脚本模板
#!/bin/bash export ORACLE_SID=orcl export ORACLE_HOME=/u01/app/oracle/product/19c/dbhome_1 export PATH=$ORACLE_HOME/bin:$PATH export NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS' LOG=/backup/rman/logs/rman_$(date +%Y%m%d).log rman target / log=$LOG <<EOF RUN { ALLOCATE CHANNEL c1 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp'; ALLOCATE CHANNEL c2 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp'; ALLOCATE CHANNEL c3 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp'; ALLOCATE CHANNEL c4 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp'; BACKUP INCREMENTAL LEVEL 1 DATABASE PLUS ARCHIVELOG; BACKUP CURRENT CONTROLFILE; RELEASE CHANNEL c1; RELEASE CHANNEL c2; RELEASE CHANNEL c3; RELEASE CHANNEL c4; } DELETE NOPROMPT OBSOLETE; CROSSCHECK BACKUP; LIST BACKUP SUMMARY; EOF这个脚本做了几件事:4通道并行、1级增量、备份归档、备份控制文件、删过期、交叉检查、列出备份摘要。日志按日期分文件,方便回溯。你可以根据自己的环境调整通道数和备份级别。
7. 我个人的几条经验总结
RMAN这东西,学的时候觉得参数多、命令杂,用久了会发现核心就那么几条:备份要能恢复、恢复要能验证、验证要定期做。我见过太多人备份脚本写得漂漂亮亮,真出事时手忙脚乱,就是因为从来没做过恢复演练。
我的建议是,每季度至少做一次完整的恢复演练,在测试环境上从零开始恢复一个库。演练的时候故意制造一些故障,比如删掉控制文件、删掉归档、模拟磁盘损坏,看看你的备份策略能不能扛住。演练中发现的每一个问题,都比生产环境出事时才发现要值钱得多。
还有一点,别迷信自动化。自动化脚本能帮你省事,但你不能不知道脚本在干什么。我见过一个脚本每天跑BACKUP DATABASE,但从来没备份过归档,因为写脚本的人以为BACKUP DATABASE会自动带上归档。结果真恢复时发现归档缺失,只能恢复到备份那个时间点,中间几天的数据全丢了。这种错误,只要你看一眼脚本就能避免。
最后分享一个小技巧:把LIST BACKUP SUMMARY和REPORT OBSOLETE的输出定期存档,出问题时对比一下,能快速定位是备份没做、还是备份被删了、还是元数据丢了。这个习惯帮我省过好几次排查时间。