去年我帮一家客户的数据库做恢复演练,DBA很自信地翻开巡检记录说“我们每天晚上都有全备”。我看了一眼RMAN输出,确实每天都有BACKUP DATABASE命令在跑。结果演练做RESTORE DATABASE的时候,目标时间点恢复怎么都不成功。最后查出来,他们的问题就藏在一句被忽略的备注里:rman 0级不等于全备,1级可能和0级一样大,但不能单独用于恢复。
这句话拆开看,每一段都是日常备份设计中最容易踩的坑。0级备份在RMAN增量备份体系里是“基线”,全备份则是另一回事;1级备份在特定条件下体积会膨胀到接近0级,却又依赖0级才能生效;备份分片不完整时恢复照样失败;删掉备份之后没有立刻补一个0级,整条恢复链就断了。这篇文章我会用实际场景把这几个问题讲透,适合所有负责Oracle数据库备份、或者正准备做恢复演练的DBA。
1. 0级备份被当成全备的代价:一次恢复演练的教训
1.1 全备份和0级备份之间,RMAN到底做了什么标记
很多人看BACKUP DATABASE和BACKUP INCREMENTAL LEVEL 0 DATABASE这两条命令,觉得输出都是“备份了整个数据库”,文件大小也差不多,于是直接叫它们全备。但从RMAN内部视角看,这两条命令的产物被记成了完全不同的东西。
BACKUP FULL DATABASE:生成的备份集类型记作FULL,RMAN不把它纳入任何增量备份链。BACKUP INCREMENTAL LEVEL 0 DATABASE:生成的备份集类型记作INCREMENTAL,LEVEL为0,RMAN会把它当作后续1级备份的基线。
判断方法很简单,查V$BACKUP_SET:
SELECT BACKUP_TYPE, INCREMENTAL_LEVEL, COUNT(*) FROM V$BACKUP_SET GROUP BY BACKUP_TYPE, INCREMENTAL_LEVEL;FULL备份的BACKUP_TYPE是D,0级增量备份的BACKUP_TYPE是I且INCREMENTAL_LEVEL为0。只看命令名称不看这个字段,是误判的根源。
1.2 恢复链路里“地基”和“楼房”的分工
可以这样理解:0级增量备份相当于盖楼之前打好的地基,1级增量备份是在地基上砌的楼层,归档日志是楼内装修的收尾工作。全备份则是另一块独立地基上的完整小楼,它不参与“0级+1级+归档”这条施工链。
如果你们的恢复策略依赖增量备份,那么0级就是整个恢复链的起点。RMAN在做恢复时,会先找一份可以作为基线的备份把数据文件还原出来,再依次应用所有可用的增量备份,最后应用归档日志。这个“基线”只能由0级备份充当,全备份没有资格。因为全备份在RMAN的元数据里没有INCREMENTAL_LEVEL标记,增量备份不会承认它是父备份。
之前遇到的一个典型事故场景是:某系统每天0级备份,DBA觉得0级就是全备,所以每周末又用BACKUP FULL DATABASE再跑一次,想着“双保险”。结果某次数据文件损坏,需要恢复到当天凌晨的状态,RMAN选择了最近的全备份作为还原源,但全备份之后只有归档日志和0级增量,恢复链路混乱,最后不得不手动拼凑多个备份集,耗时远超预期。这就是没有理解“全备份不倒扣进增量链”的代价。
1.3 如何一眼认出备份的“真实类型”
不要用备份文件大小来判断,也不要用脚本里的命令文本来判断。最稳妥的方式是看备份元数据:
LIST BACKUP SUMMARY;输出里每一行会显示备份类型、级别、设备类型、状态等。还可以细化看:
LIST BACKUP OF DATABASE;这里会明确区分Full还是Incremental,0级会标记LV=0。我记得第一次给一个团队做培训时,让大家自己执行这条命令,不少人都愣住了:“原来我们跑的是INCREMENTAL LEVEL 0,不是FULL啊。”从那之后,凡是备份脚本里写0级的地方,配置说明文件里都会明确写“这是增量基线,不是全备”。
2. 1级备份为什么可能和0级一样大,却又不能独立恢复
2.1 增量备份并不是默认的“差异去重”
1级增量备份的本意是只备份自上次备份以来发生变化的数据块。但很多人忽略了它的实现机制:Oracle在做增量备份时,要靠块变更跟踪(Block Change Tracking)或者全表扫描比对来找出“哪些块变了”。
如果启用了块变更跟踪,RMAN可以直接从跟踪文件里拿到变更块列表;如果没有启用,RMAN就老老实实扫描每一个数据文件的所有块,逐个判断SCN是否在增量窗口内。扫描大量块很快,但备份内容确实可能变得很大。
2.2 三种让1级迅速“虚胖”的场景
我归纳了三种最常遇到的1级备份膨胀场景,都符合标题里那句“1级可能和0级一样大”。
场景一:首次1级备份前,全库经历了大规模改动。比如在两次备份之间做了一次全表数据迁移、重建索引、大批量UPDATE,数据库里几乎每个块都被动过。此时的1级增量备份,本质上就是把所有已使用块再复制一遍,体积自然接近0级。
场景二:没有启用块变更跟踪。没开BCT时,RMAN必须通过全扫描判断块的变更状态。如果一个数据文件里绝大多数块都被改写,扫描结果就是“几乎全部块都要备份”,体积和0级没有区别。
场景三:长期使用累积型增量。INCREMENTAL LEVEL 1 CUMULATIVE会备份自上次0级以来所有发生变化的数据块。如果0级和1级之间隔了很长时间,而数据库每天都有大量随机写入,累积1级的体积会持续膨胀。它和差异型增量(备份自上次0级或1级以来变更的块)的行为完全不同:
| 增量类型 | 备份范围 | 适用场景 | 恢复时需要的备份数量 |
|---|---|---|---|
| 差异型1级 | 自上次0级或1级以来的变更块 | 每天变更量可控 | 0级+最近一次1级 |
| 累积型1级 | 自上次0级以来的所有变更块 | 变更分散、希望恢复时少拼1级 | 0级+当次累积1级 |
2.3 没有0级的1级,恢复时为什么“归不了位”
标题里那句“不可用于恢复”很多人理解成“1级备份不能用了”,其实是说:单独拿1级备份无法完成恢复。
原因是1级备份记录的是“相对于某个0级基线的块变化”。恢复时RMAN需要先还原0级基线的数据文件,然后再把1级里面的增量块覆盖上去。如果0级基线不存在,1级备份就成为一个没有附着点的“增量碎片”,RMAN根本不知道这些变化块相对哪个基础状态生成。
场景还原一下:备份计划里原本每周日0级、每天1级,某次误操作把0级备份的备份片删掉了,但1级备份还在。此时做RESTORE DATABASE,RMAN会提示找不到可以作为基线的备份集;即使强制把1级备份的备份片CATALOG回去,恢复也无法正确应用增量。就像你拿到了装修图纸,但毛坯房已经被拆了,图纸贴在空地上没有任何意义。
2.4 控制1级体积的日常手段
想让1级备份不“虚胖”,我的经验是按顺序处理:
- 启用块变更跟踪:
ALTER DATABASE ENABLE BLOCK CHANGE TRACKING;- 把1级备份安排在变更量最小的时段,缩短增量窗口。
- 如果变更非常分散且量很大,评估是否把1级改为0级的频率,避免硬扛累积型1级。
- 每次备份后记录
v$backup_set中BLOCKS和DATAFILE_BLOCKS的比值,监控增量备份的实际效率。
另外要说清楚:增量备份体积变大本身不一定代表备份无效,只是恢复成本和存储成本都上去了。真正致命的是“1级没有0级可依赖”这种结构性错误,体积只是警示信号。
3. 备份分片:一个备份集被切成多片时容易埋雷
3.1 备份集与备份片到底是什么关系
RMAN执行一次备份操作,产生的是备份集(Backup Set),备份集由若干个**备份片(Backup Piece)**组成。备份片才是真正写到磁盘上的物理文件。
用LIST BACKUP看控制文件元数据时,会看到类似这样的层级:
BS Key Type LV Size Device Type Elapsed Time ------ ---- -- ---------- ----------- ------------ 123 Incr 1 1.5G SBT_TAPE 00:00:25 BP Key 123456 Status: AVAILABLE Piece Name: /backup/ora_df_abcde_1_1 Piece Name: /backup/ora_df_abcde_1_2同一个备份集里有多个Piece Name,这就是分片。恢复时RMAN按备份集整体来还原数据,只要属于同一个备份集的所有备份片都在,就能正常恢复;缺失任意一片,这个备份集就不完整了。
3.2 分片数量由哪些因素决定
我比较常遇到的分片原因有三个:
- 通道数量:开了多个
ALLOCATE CHANNEL时,RMAN会并行写多个备份片。 FILESPERSET设置:控制一个备份集最多包含多少个数据文件,文件一多就可能拆成多个备份集。MAXPIECESIZE设置:比如执行BACKUP DATABASE MAXPIECESIZE 8G,超过8G的部分会被拆成新的备份片。
这是控制备份颗粒度的常用手段。分片多不一定是坏事,但它在物理管理上引入了新的风险点:任何一个备份片所在的磁盘损坏、被误删、或者写一半失败,整个备份集就不可用了。所以我一直强调给RMAN备份文件做条带化时,不要天真地以为“分散到更多磁盘就更安全”,至少要想清楚单片损坏的后果。
3.3 delete backup 与 delete obsolete 的差异,以及分片删除的正确做法
RMAN里两条删除命令经常被混淆:
DELETE BACKUP:按对象删除指定备份(比如DELETE BACKUP OF DATABASE),不管它是否过期。DELETE OBSOLETE:按保留策略删除已过期的备份。
两者的区别很关键。DELETE OBSOLETE只清理策略之外的备份,相对安全;DELETE BACKUP是主动删除,容易把还在恢复链里的基线干掉。
删除分片还有一个常见坑:只用操作系统命令直接删物理文件,没有经过RMAN。这样控制文件里对应备份片的状态仍然是AVAILABLE,但物理文件已经不存在。后续做恢复时,RMAN才会发现该备份片无法读取。正确做法是,要么通过RMAN做删除并让控制文件同步状态,要么删除后用CROSSCHECK BACKUP和DELETE EXPIRED BACKUP把元数据清理掉。
建议在备份脚本里定期加交叉检查:
rman target / <<EOF CROSSCHECK BACKUP; DELETE EXPIRED BACKUP; DELETE OBSOLETE; EOF这几行命令成本很低,但能避免“控制文件说备份在、磁盘上却找不到文件”的诡异状态。
3.4 缺少一块分片,整个备份集作废
有一次在检查备份库时,我发现某个备份集有4个备份片,其中1个状态是AVAILABLE但物理文件被归档工具误删了。当时没有做RESTORE所以没暴露问题。后来正式做恢复演练,RMAN在尝试读取这个备份集时直接报错,定位到缺失的备份片后,整个0级基线作废,当晚只能从头做一次0级。
这里要强调一个理念:RMAN备份的完整性,不是看“总体备份数量多不多”,而是看每一条恢复链的依赖是否全部可用。一个备份集缺一片,和整个备份集不存在,在恢复层面没有本质区别。
4. delete backup之后的备份链重建:为什么第一个备份必须是0级
4.1 删除备份后常见的三种“链断裂”
我在实际巡检里见过太多次“删完备份直接裸奔”的情况,归纳起来有三种常见断链:
第一种:删了0级,留了1级。这是最隐蔽的坑。因为1级备份文件还在,备份大小统计看起来也还有数据,但恢复时RMAN找不到基线,这条链实际是断的。
第二种:删了当前周期全部备份,但归档日志还在。数据文件没有基线,就算归档日志齐全,也只能从某个孤立的旧备份恢复,无法恢复到删除备份后的任意时间点。
第三种:用DELETE BACKUP误删了仍在保留策略内的最新0级。这种往往发生在手动清理时没有先看保留策略,RMAN不会替你做“这个备份被1级依赖,不能删”的强制保护。
4.2 “先做0级”为什么是唯一稳妥的顺序
恢复链的特点决定了,只要后续还要使用增量备份,那么删除备份之后重建的第一个备份必须是0级增量,而不是全备份,也不是1级。
原因很简单:全备份不会进入增量链,之后再做1级时,RMAN依然找不到0级基线;直接做1级,更是从头就没有基线。只有0级同时满足两个条件:内容上是完整的数据库映像,元数据上被RMAN识别为增量基线。这样后续叠加1级、归档日志才能恢复出正确的目标时间点。
这条原则也被我写进了备份变更的操作节拍里:任何涉及删除备份的变更,都要先确认变更后存在可用的0级备份,再执行删除动作。如果刚删了0级,那么当务之急就是安排一次新的0级,而不是补一次全备份充数。
4.3 一套可直接落地的重建命令序列与调度
假设现在是周一,你刚刚删掉了一整套旧备份,想在当天完成备份链重建。我会这么做:
- 启用块变更跟踪(如果还没启用):
ALTER DATABASE ENABLE BLOCK CHANGE TRACKING;- 做一次0级备份,带上归档日志:
rman target / <<EOF BACKUP INCREMENTAL LEVEL 0 DATABASE PLUS ARCHIVELOG DELETE INPUT; EOFPLUS ARCHIVELOG DELETE INPUT这步很重要,它同时把控制文件、归档日志纳入备份链,并清理已经进入备份集的归档日志,避免磁盘空间膨胀。
- 做一次1级备份确认链能叠加:
rman target / <<EOF BACKUP INCREMENTAL LEVEL 1 DATABASE PLUS ARCHIVELOG DELETE INPUT; EOF- 验证备份链:
RESTORE DATABASE PREVIEW; VALIDATE DATABASE;RESTORE DATABASE PREVIEW可以检查RMAN是否能解析出完整恢复路径,不需要真正执行恢复,做一次的成本很低。VALIDATE DATABASE则验证备份文件的可读性。
如果后续按天调度1级,可以在crontab里排成:
- 周日凌晨2点:0级备份
- 周一到周六凌晨2点:1级备份
- 每天凌晨3点:归档日志备份
这套节奏的核心思想就是:删除备份后的第一个备份节点固定为0级,之后的增量都依赖这个新基线。
4.4 重建之后,用恢复演练验证整条链
最后分享一个我坚持了很多年的习惯:每次重建备份链、或者改了备份策略之后,都要做一次真正的恢复演练,不是只看LIST BACKUP和日志里的“Completed”。
最简单的做法是在测试环境恢复数据库到最近一个时间点:
rman target / <<EOF STARTUP FORCE NOMOUNT; RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN RESETLOGS; EOF如果能走完这套流程,说明0级和1级组合的链路是通的。这一步跑不通,排障成本远比在备份阶段发现问题高。我见过太多生产系统平时“备份成功”但从未做过恢复演练,真出问题时才发现链条早断了。备份的真正价值从来不是“生成了多少文件”,而是“能不能在需要时恢复出期望的时间点”。