☰
Oracle RMAN全能备份脚本:全备+增量+归档清理+恢复验证实战
2026/10/3 4:47:16 网站建设 项目流程

简介:面向Oracle数据库管理员(DBA)的RMAN全能备份脚本合集,聚焦全备、增量备份与数据泵备份三大场景,帮助运维人员快速构建规范化备份流程,降低人为误操作风险,确保数据库在故障或数据丢失时能够及时恢复。整套脚本以rar压缩包形式发布,体积仅1KB,共包含5个Shell脚本,分别承担全库备份、0级增量备份、1级增量备份、数据泵逻辑导出及Linux定时调度等职责,覆盖从物理备份到逻辑备份、从手动执行到自动运行的全流程。目前已有132人学习下载,适合需要快速落地RMAN备份策略、兼顾效率与安全性的数据库运维人员参考。脚本结构清晰、参数注释明确,可根据业务实际调整备份路径、保留策略与执行周期,配合RMAN的validate命令定期验证备份集完整性,有效提升备份任务的可靠性与可维护性。

1. 先把 RMAN 脚本拆开:一套脚本管住全备、增量与归档清理

生产库里最怕的不是数据库宕了,而是宕完之后发现备份是坏的。我拆过不少 DBA 手里的 RMAN 备份脚本,最常见的状态是:全备靠手点、增量看心情、归档日志堆到磁盘满才想起来清理。这套 Oracle RMAN 全能备份脚本解决的就是这三件事——把 level 0 全备、level 1 增量、归档日志清理、数据泵逻辑备份收进同一套 shell 编排里,配上标签和日志,能直接扔进 crontab 里跑。适合手里管着单实例或备库、不想再用第三方备份软件、又被领导要求"备份必须可恢复"的 DBA。下文所有命令我都按生产环境习惯写,从策略选型一路到避坑实录,照着改就能落地。

2. 备份策略与 RMAN 参数选型:归档模式、0 级与 1 级增量怎么搭

2.1 先查数据库状态:ARCHIVELOG、FORCE LOGGING 与归档频率

RMAN 增量备份的前提是数据库必须运行在归档模式下。很多刚接手 Oracle 的运维兄弟第一件事就写备份脚本,结果跑 level 1 增量时报错,回头一查,库还在 NOARCHIVELOG。所以在写脚本之前,我一般先执行下面这段 SQL,把数据库的运行状态摸清楚:

sqlplus / as sysdba <<EOF set linesize 200 pagesize 50 col name format a20 select name, log_mode, force_logging, open_mode from v\$database; select thread#, sequence#, status, archived from v\$log order by thread#, sequence#; select sum(blocks * block_size) / 1024 / 1024 as avg_mb_per_log from v\$archived_log where first_time > sysdate - 7; EOF

这段 SQL 看三件事。第一,log_mode必须是ARCHIVELOG,不是的话先shutdown immediate然后startup mount再alter database archivelog;,否则后面所有增量策略都无从谈起。第二,force_logging建议为YES,特别是库里有NOLOGGING操作(比如 direct load、索引重建)时,不强制日志会导致增量备份丢失这部分变化。第三,avg_mb_per_log是过去 7 天每个归档的平均大小,这个数字直接决定归档删除策略里completed before的保留天数——归档越多,保留窗口越短,不然备份盘很快被撑满。

2.2 RMAN 增量机制:0 级全备、1 级增量与快照控制文件

RMAN 的增量备份有两个层级,level 0和level 1。level 0 是全量基线,备份所有数据文件块;level 1 是增量,只备份自上次备份以来发生变化的数据块。注意一点:level 1 分为差异增量(incremental level 1,默认)和累积增量(incremental level 1 cumulative)。差异增量只备份从上一次 level 1 或 level 0 之后的变化,恢复时要按时间顺序依次应用;累积增量备份从上一次 level 0 之后的所有变化,恢复时只需要一个累积增量加一个 level 0,链路更短。生产库里我一般用差异增量,因为备份量小、跑得快,前提是你得保证恢复链完整。

还有一个经常被忽略的参数是快照控制文件。RMAN 备份期间需要读控制文件的一致性快照,默认在 Oracle 家目录的dbs下,但有备库或 DG 环境时,这个快照路径不能和主库冲突。建议在备份脚本里显式指定快照控制文件位置,避免在多个目录间来回找:

rman target / <<EOF configure snapshot controlfile name to '/u01/backup/rman/snapcf_orcl.f'; configure controlfile autobackup on; configure controlfile autobackup format for device type disk to '/u01/backup/rman/ctl_%F.bak'; EOF

第一行指定快照控制文件路径,第二行开启控制文件自动备份,第三行设置自动备份的格式。%F是 RMAN 专门为控制文件设计的占位符,它自动带上 DBID、日期和序列号,恢复时 RMAN 能据此自动定位最新的控制文件备份。这三条配置写进脚本里,等于给备份加了一道保险:哪怕控制文件整个没了,也能从最后一份自动备份恢复元数据。

2.3 备份目录、保留策略与运行窗口评估

备份目录规划这块,我踩过一次大坑:一开始把所有备份集堆在一个目录,时间一长,list backup里能看到几百个备份集,磁盘满到连归档日志都写不进去。后来按日期分子目录,目录结构固定成下面这样:

路径用途保留策略
/u01/backup/rman/YYYYMMDD/RMAN 备份集保留 7 天,过期由 RMAN 删除
/u01/backup/rman/ctl_%F.bak控制文件自动备份保留 14 天
/u01/backup/expdp/YYYYMMDD/数据泵导出文件保留 7 天
/u01/backup/logs/脚本运行日志保留 30 天

保留策略用 RMAN 配置而非操作系统脚本去删,这是关键。很多人习惯写个find /u01/backup -mtime +7 -exec rm -rf {},这样干会出大事——RMAN 记录里的备份集文件还在,但物理文件已经没了,恢复时报ORA-19809找不到备份。正确做法是让 RMAN 自己管理生命周期:

rman target / <<EOF configure retention policy to recovery window of 7 days; EOF

这条命令的含义是:保留最近 7 天内能完成恢复所需的全部备份。比如你周日做了 level 0,周一到周六做了 6 个 level 1,那周四的 level 1 在周日之前是不能删的,因为恢复链需要它。RMAN 会在满足 7 天恢复窗口后才把过期的备份集标记为EXPIRED。至于运行窗口评估,我一般查v$rman_status里每次备份的耗时和大小,再对比业务低谷期长度:

select session_key, input_type, status, to_char(start_time, 'mm-dd hh24:mi') start_t, to_char(end_time, 'hh24:mi') end_t, round(input_bytes/1024/1024/1024, 1) input_gb, round(output_bytes/1024/1024/1024, 1) output_gb from v$rman_status where start_time > sysdate - 7 order by session_key;

看到输出后,如果全备要跑 3 个小时,而业务低谷只有 2 小时,就得考虑开并行通道或者压缩。这套脚本里默认开两个 channel 并启用compressed backupset,压缩率在数据仓库类库上通常能到 3:1 到 5:1,能显著缩短备份窗口。

3. 脚本编排实战:从全备函数到归档删除与数据泵一体化

3.1 环境变量、目录结构与脚本骨架

脚本的核心是把 RMAN 的重复操作封装成一个带参数的 shell 函数。先看骨架,我建议所有路径变量集中放在脚本头部:

#!/bin/bash # rman_backup.sh - 全能备份脚本 # 用法: sh rman_backup.sh 0 # level 0 全备 # sh rman_backup.sh 1 # level 1 增量 # sh rman_backup.sh exp # 数据泵导出 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' BACKUP_BASE=/u01/backup/rman/$(date +%Y%m%d) LOG_DIR=/u01/backup/logs EXPDP_DIR=/u01/backup/expdp/$(date +%Y%m%d) DATE_TAG=$(date +%Y%m%d_%H%M%S) LEVEL="${1:-0}"

这里有几个细节不能省。NLS_DATE_FORMAT必须设为yyyy-mm-dd hh24:mi:ss,因为 RMAN 日志里的时间戳不设这个格式会很别扭,排错时看不清楚归档的完成时间。BACKUP_BASE和EXPDP_DIR按天分开,避免备份集堆积在同一个目录下,也为后面crosscheck和删除策略提供清晰的路径来源。LEVEL参数允许你在命令行直接指定备份级别,crontab 里调用时就不需要维护多个脚本了。

3.2 全备与增量主体:channel、format、tag 怎么配

备份主体写成函数do_rman_backup,用if [ "$LEVEL" = "0" ]区分 level 0 和 level 1,跑完通知外层函数做日志检查:

do_rman_backup() { mkdir -p "${BACKUP_BASE}" "${LOG_DIR}" rman target / log="${LOG_DIR}/rman_${LEVEL}_${DATE_TAG}.log" <<EOF run { allocate channel c1 device type disk format '${BACKUP_BASE}/b_%U.bak'; allocate channel c2 device type disk format '${BACKUP_BASE}/b_%U.bak'; backup incremental level ${LEVEL} database tag 'INC${LEVEL}_${DATE_TAG}'; backup archivelog all not backed up 1 times delete input; backup current controlfile; release channel c1; release channel c2; } EOF if [ $? -ne 0 ]; then echo "[ERROR] RMAN ${LEVEL} backup failed at $(date)" >> "${LOG_DIR}/error_summary.log" fi }

逐行说参数。allocate channel c1 device type disk format '...%U.bak'里的%U是 RMAN 自动生成的唯一字符串,保证同一通道上的多个备份集文件名不冲突;开两个 channel 时,RMAN 会把备份集分片写到两个通道上,并发读写能提升吞吐,但通道数不要超过cpu_count,否则反而争抢 IO。backup incremental level ${LEVEL} database tag 'INC...',tag很重要,恢复时用list backup of database tag 'INC0_...'就能在众多备份里精确找到你要的那一个,不用去翻时间戳。

backup current controlfile这一行容易被新手忽略,它的作用是在每次备份结束后强制单独备份一次控制文件。加上前面配置里的controlfile autobackup on,控制文件实际上会被备份两次,冗余换来的是恢复时更高的容错。

3.3 归档日志删除策略:delete input、not backed up 与 completed before 的区别

归档删除策略是脚本里最容易翻车的部分,热搜词里那条 "rman delete archive,from,until,before区别" 问的正是这里。先把我脚本里用的这行拆开讲:

backup archivelog all not backed up 1 times delete input;

backup archivelog all表示把所有归档都纳入备份集;not backed up 1 times表示只处理那些从未被备份过的归档;delete input表示这些归档在进入备份集成功后立即删除。三个条件合在一起的效果是:每天跑一次增量时,只备份新增的归档,备份成功后立刻释放磁盘空间,不会误删尚未纳入备份集的归档。

与之相对的是delete archivelog until time 'sysdate-3'和delete archivelog completed before 'sysdate-3',它们不关心归档有没有备份过,只看时间。我在生产环境只在一种场景下用时间删除:归档堆积已经跟不上备份节奏时,作为兜底命令清理三天前的归档。但要注意until time、completed before、from ... until ...的区别:completed before是按归档完成时间过滤,until time在 delete 时实际也是按完成时间算,两者差异很微妙;from sequence ... until sequence则是按归档序号范围,常在归档日志有断档时用来定向清理。日常脚本里不要混用,我始终以not backed up 1 times delete input为主线,时间删除只做兜底。

3.4 数据泵导出:把物理备份和逻辑备份放回同一个调度

RMAN 物理备份解决的是"整个库坏了能不能恢复"的问题,数据泵逻辑备份解决的是"某张表被误删了能不能快速捞回来"的问题。很多生产事故的恢复诉求其实只需要捞一张表,用 RMAN 做表级恢复比较笨重,所以这套脚本里我把 expdp 也纳入了统一调度:

do_expdp_backup() { mkdir -p "${EXPDP_DIR}" expdp \"/ as sysdba\" directory=DATA_PUMP_DIR \ dumpfile=expdp_${DATE_TAG}.dmp \ logfile=expdp_${DATE_TAG}.log \ schemas=SCOTT,APP_USER \ parallel=2 compression=ALL \ cluster=N }

expdp "/ as sysdba"的写法是为了避免把口令写在命令行里,进程列表里不会暴露密码。schemas=SCOTT,APP_USER是你想导出的业务模式列表,按需调整,别全库导出,否则耗时长、文件大,还容易卡在统计信息收集上。parallel=2是导出并行度,一般不超过 CPU 核数,IO 紧张的系统建议降到 1;compression=ALL对 dump 文件启用压缩,导出文件体积能小一半以上;cluster=N是 12c 以上版本必须加的,否则 RAC 环境下 expdp 会尝试全集群调度,容易报错。

数据泵导出的安全性有一个细节:expdp 导出期间如果表数据在变化,导出的是读一致性的快照,不会锁表,但导出文件可能很大,I/O 和 RMAN 备份叠加时要注意错峰。所以我一般把 RMAN 放在夜里 1 点,expdp 放在凌晨 3 点,错开 IO 峰值。

4. 脚本部署与自动调度:部署清单、crontab 与首次跑批检查

4.1 部署前要确认的四个前置条件

脚本不是拷过去就能跑,部署前我强制自己过一遍以下四项检查,缺一项后面都会在半夜里把你叫醒。第一,ORACLE_SID和ORACLE_HOME是否正确,特别是 RAC 环境,两个实例的ORACLE_HOME路径可能不同。第二,备份目录的属主和权限:目录必须归oracle用户所有,且空间足够,我用df -h /u01/backup确认。第三,数据库可登录性,sqlplus / as sysdba不能需要密码,否则 crontab 里跑不起来。第四,归档模式确认,把 2.1 那段 SQL 跑一遍,输出里log_mode不是ARCHIVELOG就先别部署脚本。

部署时,我习惯先在命令行手动跑一次 level 0,不要直接上 crontab。原因是:手动跑能看到 RMAN 日志输出到终端,第一时间发现路径错误、权限问题、归档异常,这些在 crontab 里只会静默地写进日志文件,等到第二天早上看到error_summary.log里挂着一条失败记录,还得回头排查是哪一步出的问题。

4.2 首次跑批与日志核查

首次跑批建议用 nohup 方式在后台执行,并观察日志尾部:

cd /u01/scripts nohup sh rman_backup.sh 0 > /u01/backup/logs/nohup_$(date +%Y%m%d).log 2>&1 & tail -f /u01/backup/logs/rman_0_$(date +%Y%m%d_*).log

脚本跑完后,重点看三个地方。第一,日志最后一行是否出现RMAN-03009或ORA-19506,这说明备份过程中有文件级错误;第二,list backup summary;里是否出现了A 0或A 1的备份记录,A 代表available,0/1 代表 level;第三,检查v$rman_status的状态是否为COMPLETED。光看LOG_DIR里有没有生成日志文件不算验证,我见过日志文件生成了但内容里全是错误的案例,所以核查一定要看内容,不能看存在性。

4.3 crontab 编排:周日全备、周一至周六增量、每天数据泵

调度策略我采用"周日全备,周一至周六增量,每天数据泵"的组合,这是单实例生产库性价比最高的方案。全备放周日凌晨 1 点,增量放其他凌晨 1 点,数据泵放凌晨 3 点,既错峰又保证每天早上都有一个可用的逻辑备份:

# RMAN level 0 every Sunday at 01:00 0 1 * * 0 /u01/scripts/rman_backup.sh 0 >> /u01/backup/logs/cron.log 2>&1 # RMAN level 1 from Monday to Saturday at 01:00 0 1 * * 1-6 /u01/scripts/rman_backup.sh 1 >> /u01/backup/logs/cron.log 2>&1 # Data pump every day at 03:00 0 3 * * * /u01/scripts/rman_backup.sh exp >> /u01/backup/logs/cron.log 2>&1

crontab 里有个细节:环境变量。crontab 默认 PATH 只有/usr/bin:/bin,Oracle 的环境变量必须在脚本里自己 export,这就是脚本头部那四行export的作用。另外日志双边记录,cron.log只记录脚本启停,具体 RMAN 日志在rman_*_*.log里,两相对照才能定位问题。我每个月初还会额外加一条清理任务,把超过 30 天的日志归档压缩,防止日志目录本身把磁盘吃掉。

5. RMAN 备份避坑实录:磁盘满、备份集失效与恢复不到的高频问题

5.1 归档堆积导致备份中途满盘:ORA-19506 与删除策略失效

现象:RMAN 备份跑到 60% 左右报ORA-19506: failed to create sequential file,紧接着磁盘空间不足,备份失败。

原因:归档日志删除策略失效。最常见的是脚本里只写了backup archivelog all delete input,但漏掉了not backed up 1 times这个条件。这会导致每次备份时,RMAN 把包括昨天已经备份过的归档再备份一遍,磁盘空间被重复备份消耗;更隐蔽的是,有些归档是在 RMAN 之外被其他工具归档的,RMAN 元数据里没有记录,delete input就永远不会删除它们。

解决:把归档备份统一改成backup archivelog all not backed up 1 times delete input;。如果当前磁盘已经被堆满,先手工清理,再用兜底命令delete noprompt archivelog all completed before 'sysdate-3';清掉三天前的归档,让备份先跑起来。从那以后,我把归档备份和delete绑死在一个语句里,不再单独写删除命令。

5.2 手动删备份文件导致备份集失效:ORA-19809 与 crosscheck

现象:系统管理员清理磁盘时把/u01/backup下几天前的备份集文件删了,但没动数据库。下一次增量备份正常,list backup也还能看到被删的备份集记录,恢复到该时间点时报ORA-19809: error in backup/restore file context,找不到物理文件。

原因:备份集记录在控制文件或恢复目录里,物理文件在文件系统里。RMAN 默认不会实时感知物理文件是否存在,删除操作超出了 RMAN 的管理范围,元数据和物理文件的对应关系就断了。这是文件系统和数据库管理职责分离导致的典型事故。

解决:禁止任何人在 RMAN 之外手动删除备份文件。清理动作必须通过delete obsolete或crosscheck来执行。我在脚本里加了一段每周自动执行的交叉校验:

rman target / <<EOF crosscheck backup; delete noprompt expired backup; crosscheck archivelog all; delete noprompt expired archivelog all; EOF

crosscheck的作用是让 RMAN 逐一核对物理文件是否存在,不存在的就标记为EXPIRED,随后delete expired把这些记录从控制文件里清掉。加了这段之后,list backup里的记录永远和物理文件一致,恢复时不会踩到"记录在但文件不在"的坑。

5.3 没有 0 级基础就拉 1 级增量:RMAN-20207 与基线管理

现象:新环境部署脚本后直接执行sh rman_backup.sh 1,RM AN 报RMAN-20207: unable to find a base backup,无法生成 level 1 增量。

原因:level 1 增量必须要有一个 level 0 基线存在,RMAN 才能基于它计算变化块。很多人在测试环境里先跑了增量验证,直接推到生产,结果生产库没有 level 0 基线,跑了个寂寞。

解决:首次部署时先强制跑一次 level 0。更稳妥的做法是在脚本里加一个基线判断,每次跑 level 1 之前先查有没有最近的 level 0:

latest_base=$(rman target / <<EOF | grep -c "INC0" list backup of database tag like 'INC0%' completed after 'sysdate-7'; EOF ) if [ "${latest_base}" -eq 0 ]; then echo "No level 0 in last 7 days, force level 0 backup now." sh "$0" 0 exit $? fi

这段逻辑的用意是:如果最近 7 天内找不到任何 level 0 备份记录,就自动降级为 level 0 全备,保证增量链不中断。注意grep -c "INC0"是简单计数,实际使用时可以更严格地匹配 completed 时间,但思路一致——宁可多跑一次全备,也别让增量备份在空基线上失败。

5.4 不开控制文件自动备份:控制文件与 SPFILE 全部丢失后的尴尬

现象:控制文件所在的磁盘损坏,所有控制文件副本丢失,同事试图用最近一次备份来恢复,却发现 RMAN 连控制文件备份都没有,restore controlfile找不到目标。

原因:脚本里只备份了数据文件和归档,没单独备份控制文件,也没开controlfile autobackup。控制文件一旦全部丢失,RMAN 连"从哪里开始恢复"的元数据都没了。这个场景下,数据文件再多备份也没用,整个恢复流程直接瘫痪。

解决:脚本里的backup current controlfile配合配置里的configure controlfile autobackup on双保险恢复。更完整的做法是在备份脚本中单独构建一个控制文件恢复步骤。实际恢复时,如果连控制文件备份都没有,只能用dbms_backup_restore从数据文件头里重建控制文件,那一刻你会深刻体会到什么叫"备份做了,但等于没做"。所以我现在的习惯是:每台库部署脚本后,第一件事就是执行list backup of controlfile;确认控制文件备份确实存在,而不是默认它一定存在。

6. 恢复验证闭环:用 restore validate 把备份盘成可交付资产

备份脚本跑通不等于备份可用,这个结论我是在一次真实的恢复演练里被教训出来的。那次的备份日志里连续一周都是COMPLETED,但演练恢复时restore database直接报文件缺失——原因是某个数据文件从备份集里处于损坏状态,备份过程却没感知。从那以后,我每个月初都会在脚本里加一段强制执行的restore database validate:

rman target / <<EOF run { allocate channel v1 device type disk; allocate channel v2 device type disk; restore database validate; release channel v1; release channel v2; } EOF

restore validate不会真正恢复数据,它只做两件事:逐一读取备份集里的数据块,校验块是否损坏、是否缺失;顺带验证整条恢复链——从 level 0 基线到最近的 level 1 增量是否完整。它的输出里如果出现ORA-01547或ORA-01173,说明备份集本身有问题,必须立刻处理。注意restore validate是只读操作,不会动数据库文件,可以在业务低峰期放心跑。

对于备份集级别的定向验证,不想全库校验时可以指定备份集。先查备份集号,再单独验证:

list backup of database summary; validate backupset 1234;

validate backupset针对单个备份集做物理校验,速度快很多,适合日常抽检。我的验证套路是:每月一次restore database validate跑全链路,每周随机挑两个备份集做validate,每次验证结果都追加到当月报告中。恢复演练脚本独立于备份脚本存放,路径固定为/u01/scripts/rman_validate.sh,这样任何接手的 DBA 都能在三分钟内跑完验证、拿到结论。备份的价值从来不在日志里那行 "COMPLETED",而在于你真正执行restore的那几分钟里,它能不能把数据完整还给你。这套验证习惯我一直保持到现在,每个月雷打不动跑一遍,也是一名 DBA 给自己留的后悔药。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询