一、一次未备份的代价
曾经有个项目的 Oracle 数据库运行了三年没出过事,所有人默认"它很稳定"。直到某天运维人员执行了一条 DELETE FROM sales_log WHERE 1=1;——没加 WHERE 条件,没开事务回滚,直接 commit 了。等业务方发现数据全没了,查了半天发现:RMAN 备份配置是有的,但归档日志满了之后自动停了,最后一次全备是 47 天前。
那次恢复花了 4 天,从 47 天前的全备 + 残缺的归档日志拼回了 47 天前的数据,中间 47 天的交易全丢了。
从那以后我确立了一条铁律:备份策略不算数,恢复验证才算数。 配好了 RMAN 不代表备份在跑,备份在跑不代表能恢复——只有定期做恢复演练,才能保证关键时刻数据不丢。
二、Oracle 备份体系全景
Oracle 数据保护
│
┌──────────────┼──────────────┐
▼ ▼ ▼
物理备份 逻辑备份 闪回技术
(RMAN) (expdp) (Flashback)
│ │ │
┌──────┼──────┐ │ ┌────┼────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼
全备 增量 归档 逻辑导出 闪回 闪回 闪回
日志 .dmp 查询 表 数据库
经验法则:物理备份(RMAN)是主力,逻辑导出(expdp)是补充,闪回是应急。三者不可互相替代——RMAN 恢复粒度粗但速度快,expdp 可按表恢复但慢,闪回依赖 UNDO/闪回日志有时效限制。
三、RMAN 备份策略设计
3.1 0 级 + 1 级增量策略
生产环境推荐 每周一次 0 级全备 + 每天一次 1 级增量 + 持续归档:
# rman target / << 'EOF'
# === 0 级全量备份(每周日)===
RUN {
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
CONFIGURE CONTROLFILE AUTOBACKUP ON;
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '/data/backup/%F';
CONFIGURE DEVICE TYPE DISK PARALLELISM 4;
BACKUP INCREMENTAL LEVEL 0
DATABASE PLUS ARCHIVELOG
TAG 'WEEKLY_FULL'
FORMAT '/data/backup/db_lvl0_%U'
SECTION SIZE 2G; -- 大文件分片并行
BACKUP CURRENT CONTROLFILE
FORMAT '/data/backup/cf_%U';
DELETE OBSOLETE;
}
# === 1 级差异增量(周一至周六)===
RUN {
BACKUP INCREMENTAL LEVEL 1
DATABASE PLUS ARCHIVELOG
TAG 'DAILY_INCR'
FORMAT '/data/backup/db_lvl1_%U'
SECTION SIZE 2G;
DELETE NOPROMPT OBSOLETE;
DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK;
}
EOF
3.2 增量合成(Block Change Tracking)
增量备份默认需要扫描整个数据文件来找出变化的块,大库可能要几小时。开启 BCT 后只需扫描变化块:
-- 开启块变更跟踪
ALTER DATABASE ENABLE BLOCK CHANGE TRACKING
USING FILE '/data/oracle/oradata/ORCLCDB/bct.f';
-- 验证
SELECT filename, status, bytes FROM v$block_change_tracking;
-- 效果:1 级增量从 3 小时 → 20 分钟(取决于变化量)
3.3 归档日志管理
-- 查看归档日志产生速率
SELECT
TRUNC(completion_time) AS day,
COUNT(*) AS log_count,
ROUND(SUM(blocks * block_size)/1024/1024/1024, 2) AS size_gb
FROM v$archived_log
WHERE completion_time > SYSDATE - 7
GROUP BY TRUNC(completion_time)
ORDER BY day;
-- 设置归档日志保留策略:至少保留到最近一次增量备份之后
RMAN> CONFIGURE ARCHIVELOG DELETION POLICY TO
BACKED UP 2 TIMES TO DEVICE TYPE DISK;
3.4 闪回恢复区(FRA)容量规划
-- 设置 FRA
ALTER SYSTEM SET db_recovery_file_dest_size = 100G;
ALTER SYSTEM SET db_recovery_file_dest = '/data/oracle/fast_recovery_area';
-- 监控 FRA 使用率
SELECT
name,
SPACE_LIMIT / 1024/1024/1024 AS limit_gb,
SPACE_USED / 1024/1024/1024 AS used_gb,
SPACE_RECLAIMABLE / 1024/1024/1024 AS reclaimable_gb,
ROUND(SPACE_USED / SPACE_LIMIT * 100, 1) AS pct_used
FROM v$recovery_file_dest;
-- 告警:FRA 使用率超 80% 时必须扩容或清理
踩坑提示:FRA 满了之后 Oracle 会挂起所有 DML 操作(归档日志写不进去),而不是自动清理旧文件。必须在监控告警里设 80% 阈值提前处理。
四、定时备份脚本
4.1 crontab 配置
# Oracle 用户的 crontab
# 每周日凌晨 1:00 全备
0 1 * * 0 /home/oracle/scripts/rman_backup.sh level0 >> /home/oracle/logs/rman_lvl0.log 2>&1
# 周一至周六凌晨 1:00 增量
0 1 * * 1-6 /home/oracle/scripts/rman_backup.sh level1 >> /home/oracle/logs/rman_lvl1.log 2>&1
# 每 4 小时检查归档日志空间
0 */4 * * * /home/oracle/scripts/check_archive.sh >> /home/oracle/logs/arch_check.log 2>&1
4.2 rman_backup.sh
#!/bin/bash
# rman_backup.sh —— Oracle RMAN 备份脚本
# 用法: rman_backup.sh level0|level1
export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
export ORACLE_SID=ORCLCDB
export PATH=$ORACLE_HOME/bin:$PATH
export NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'
LEVEL=$1
TAG="DAILY_INCR"
if [ "$LEVEL" = "level0" ]; then
TAG="WEEKLY_FULL"
fi
BACKUP_DIR="/data/backup"
LOGFILE="/home/oracle/logs/rman_${LEVEL}_$(date +%Y%m%d_%H%M%S).log"
rman target / log=${LOGFILE} << EOF
RUN {
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
CONFIGURE CONTROLFILE AUTOBACKUP ON;
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '${BACKUP_DIR}/%F';
CONFIGURE DEVICE TYPE DISK PARALLELISM 4;
BACKUP INCREMENTAL LEVEL ${LEVEL#level}
DATABASE PLUS ARCHIVELOG
TAG '${TAG}'
FORMAT '${BACKUP_DIR}/db_${LEVEL}_%U'
SECTION SIZE 2G;
SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT';
BACKUP CURRENT CONTROLFILE FORMAT '${BACKUP_DIR}/cf_%U';
BACKUP SPFILE FORMAT '${BACKUP_DIR}/sp_%U';
DELETE NOPROMPT OBSOLETE;
DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK;
}
EXIT;
EOF
# 检查备份结果
if grep -q "Finished backup" ${LOGFILE} && ! grep -q "RMAN-" ${LOGFILE}; then
echo "[$(date)] ${LEVEL} backup SUCCESS" >> ${LOGFILE}
# 发送成功通知
echo "Oracle ${LEVEL} backup succeeded on $(hostname)" | \
mail -s "Oracle Backup OK" ops@scrcx.cn
else
echo "[$(date)] ${LEVEL} backup FAILED" >> ${LOGFILE}
# 发送告警
tail -50 ${LOGFILE} | mail -s "Oracle Backup FAILED" ops@scrcx.cn
fi
五、Bareos SBT 集成:备份到磁带/云存储
5.1 为什么用 SBT
RMAN 默认备份到本地磁盘,但磁盘会满、会坏。通过 SBT(System Backup to Tape)接口,RMAN 可以直接把备份写到 Bareos 管理的磁带库或云存储,实现异地容灾。
Oracle RMAN ──→ oracle-fd.so ──→ Bareos Director ──→ 存储后端
(SBT API) (Bareos 插件) (调度+索引) (磁盘/磁带/云)
5.2 安装 Oracle SBT 插件
# 在 Oracle 服务器上安装 Bareos Oracle 插件
# 文件:oracle-fd.so, libbareossbt.so
cp oracle-fd.so /opt/oracle/product/19c/dbhome_1/lib/
cp libbareossbt.so /opt/oracle/product/19c/dbhome_1/lib/
# 配置 Oracle 能找到 SBT 库
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
# 验证 SBT 库可用
rman target / << 'EOF'
ALLOCATE CHANNEL c1 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so';
RELEASE CHANNEL c1;
EOF
5.3 RMAN 通过 SBT 备份到 Bareos
# rman target / << 'EOF'
# 通过 SBT 通道做全备
RUN {
ALLOCATE CHANNEL c1 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so'
FORMAT 'ORCLCDB_full_%U';
ALLOCATE CHANNEL c2 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so'
FORMAT 'ORCLCDB_full_%U';
BACKUP DATABASE PLUS ARCHIVELOG;
RELEASE CHANNEL c1;
RELEASE CHANNEL c2;
}
EOF
5.4 Bareos 侧配置
# /etc/bareos/bareos-dir.d/fileset/oracle-fs.conf
FileSet {
Name = "Oracle-RMAN"
Include {
Options {
signature = MD5
compression = GZIP
}
# SBT 写入的临时路径
File = "/tmp/rman_sbt"
}
}
# /etc/bareos/bareos-dir.d/job/oracle-backup.conf
Job {
Name = "OracleBackup"
JobDefs = "DefaultJob"
Client = "oracle-fd" # Oracle 服务器上的 file daemon
FileSet = "Oracle-RMAN"
Schedule = "OracleWeekly"
Write Bootstrap = "/var/lib/bareos/oracle.bsr"
# 插件参数
RunScript {
RunsOnClient = yes
RunsWhen = Before
Command = "su - oracle -c 'rman target / @/home/oracle/scripts/sbt_backup.rman'"
}
}
六、恢复实战
6.1 恢复场景与对应方法
| 场景 | 方法 | 停机时间 | 数据丢失 |
|---|---|---|---|
| 误删表(几分钟内) | Flashback Table | 秒级 | 0 |
| 误删行(UNDO 未过期) | Flashback Query | 秒级 | 0 |
| 误 drop 表 | Flashback Drop / 回收站 | 分钟级 | 0 |
| 数据文件损坏 | RMAN 恢复单个文件 | 分钟~小时 | 0(有归档) |
| 控制文件损坏 | RMAN 恢复控制文件 | 小时级 | 0 |
| 整库恢复到指定时间点 | RMAN PITR | 小时级 | 到指定时间 |
| 整库灾难恢复 | RMAN 全量恢复 | 小时级 | 最后归档时间 |
6.2 Flashback 应急恢复
-- 误删表数据(5 分钟内发现)
SELECT * FROM sales_log
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '10' MINUTE);
-- 恢复
FLASHBACK TABLE sales_log TO TIMESTAMP
(SYSTIMESTAMP - INTERVAL '10' MINUTE);
-- 前提:行移动必须开启
ALTER TABLE sales_log ENABLE ROW MOVEMENT;
-- 误 drop 表(回收站恢复)
SELECT object_name, original_name FROM recyclebin;
FLASHBACK TABLE sales_log TO BEFORE DROP;
-- 闪回整个数据库(需要闪回日志)
-- 前提:ALTER DATABASE FLASHBACK ON;
FLASHBACK DATABASE TO TIMESTAMP
(SYSTIMESTAMP - INTERVAL '1' HOUR);
6.3 RMAN 完全恢复(数据文件损坏)
# 场景:/data/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf 损坏
# 1. 启动到 mount 状态
sqlplus / as sysdba << 'EOF'
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
EOF
# 2. RMAN 恢复损坏的数据文件
rman target / << 'EOF'
RUN {
# 恢复指定数据文件
RESTORE DATAFILE 5;
RECOVER DATAFILE 5;
# 或按文件路径恢复
# RESTORE DATAFILE '/data/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf';
# RECOVER DATAFILE '/data/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf';
}
# 3. 打开数据库
SQL 'ALTER DATABASE OPEN';
EOF
6.4 RMAN PITR 时间点恢复
# 场景:上午 10:30 有人误执行了 TRUNCATE TABLE sales_log
# 需要恢复到 10:29:59
rman target / << 'EOF'
RUN {
# 设置时间点(注意 NLS_DATE_FORMAT)
SET UNTIL TIME "TO_DATE('2026-07-13 10:29:59','YYYY-MM-DD HH24:MI:SS')";
# 恢复整个数据库到该时间点
RESTORE DATABASE;
RECOVER DATABASE;
}
SQL 'ALTER DATABASE OPEN RESETLOGS';
EOF
重要:PITR 恢复后必须
OPEN RESETLOGS,会重置在线日志序列。恢复前建议先做一次全备保护当前状态。
6.5 从 Bareos SBT 恢复
# 场景:本地磁盘备份全部丢失,需要从 Bareos 恢复
# 1. 先恢复 spfile 和控制文件
rman target / << 'EOF'
# 启动到 nomount(用默认 spfile 或 pfile 启动)
STARTUP NOMOUNT;
# 从 SBT 恢复 spfile
RUN {
ALLOCATE CHANNEL c1 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so';
RESTORE SPFILE FROM AUTOBACKUP;
RELEASE CHANNEL c1;
}
# 重启到 nomount(用恢复的 spfile)
SHUTDOWN IMMEDIATE;
STARTUP NOMOUNT;
# 从 SBT 恢复控制文件
RUN {
ALLOCATE CHANNEL c1 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so';
RESTORE CONTROLFILE FROM AUTOBACKUP;
RELEASE CHANNEL c1;
}
# 进入 mount 状态
ALTER DATABASE MOUNT;
# 2. 恢复数据文件
RUN {
ALLOCATE CHANNEL c1 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so';
ALLOCATE CHANNEL c2 DEVICE TYPE 'SBT_TAPE'
PARMS='SBT_LIBRARY=libbareossbt.so';
RESTORE DATABASE;
RECOVER DATABASE;
}
# 3. 打开数据库
ALTER DATABASE OPEN RESETLOGS;
EOF
七、恢复验证清单
备份不验证等于没备份。每月至少做一次恢复演练:
#!/bin/bash
# verify_backup.sh —— 备份验证脚本
export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
export ORACLE_SID=ORCLCDB
# 1. 验证备份集完整性(不实际恢复)
rman target / << 'EOF'
RESTORE DATABASE VALIDATE;
RESTORE ARCHIVELOG ALL VALIDATE;
RESTORE CONTROLFILE VALIDATE;
RESTORE SPFILE VALIDATE;
EOF
# 2. 检查备份集中是否有损坏块
rman target / << 'EOF'
BACKUP VALIDATE CHECK LOGICAL DATABASE;
EOF
# 3. 查询验证结果
sqlplus -S / as sysdba << 'EOF'
SELECT * FROM v$database_block_corruption;
-- 如果有记录,说明备份中有坏块,需要立即重新备份
EOF
# 4. 验证备份保留策略
rman target / << 'EOF'
REPORT NEED BACKUP; -- 哪些文件需要备份
REPORT OBSOLETE; -- 哪些备份可以删除
LIST BACKUP SUMMARY; -- 备份总览
LIST ARCHIVELOG ALL; -- 归档日志列表
EOF
# 5. 恢复测试(在测试环境)
# 将备份恢复到测试库,验证数据可读
rman target / << 'EOF'
RUN {
SET NEWNAME FOR DATAFILE 1 TO '/tmp/test/system01.dbf';
SET NEWNAME FOR DATAFILE 2 TO '/tmp/test/sysaux01.dbf';
SET NEWNAME FOR DATAFILE 3 TO '/tmp/test/undotbs01.dbf';
SET NEWNAME FOR DATAFILE 5 TO '/tmp/test/users01.dbf';
RESTORE DATABASE TO SCN 1234567;
SWITCH DATAFILE ALL;
RECOVER DATABASE TO SCN 1234567;
}
EOF
八、监控告警
8.1 关键监控指标
-- 备份成功率(最近 7 天)
SELECT
status,
COUNT(*) AS count
FROM v$rman_backup_job_details
WHERE start_time > SYSDATE - 7
GROUP BY status;
-- 期望:COMPLETED 全部,没有 FAILED
-- 最近一次备份时间
SELECT
'DB_FULL' AS type,
MAX(completion_time) AS last_backup
FROM v$backup_set WHERE incremental_level = 0
UNION ALL
SELECT
'DB_INCR',
MAX(completion_time)
FROM v$backup_set WHERE incremental_level = 1
UNION ALL
SELECT
'ARCHIVELOG',
MAX(completion_time)
FROM v$backup_set WHERE backup_type = 'L';
-- 备份大小趋势
SELECT
TRUNC(completion_time) AS day,
ROUND(SUM(bytes)/1024/1024/1024, 2) AS backup_size_gb
FROM v$backup_set
WHERE completion_time > SYSDATE - 30
GROUP BY TRUNC(completion_time)
ORDER BY day;
-- 归档日志 GAP 检查(Data Guard 环境)
SELECT * FROM v$archive_gap;
8.2 告警规则
CRITICAL:
- 备份失败(连续 2 次)
- FRA 使用率 > 90%
- 归档日志空间不足
- 数据块损坏
WARNING:
- 最近一次全备 > 8 天
- FRA 使用率 > 80%
- 归档日志未备份 > 12 小时
九、避坑清单
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 测试环境无归档模式 | RMAN 无法增量备份 | ALTER DATABASE ARCHIVELOG;(需重启) |
| 2 | FRA 满 | DML 全部挂起 | 监控 80% 告警 + 定期清理过期备份 |
| 3 | RESETLOGS 后旧备份失效 | 恢复时报备份集不匹配 | RESETLOGS 后立即做 0 级全备 |
| 4 | 控制文件丢失且无自动备份 | 无法定位备份集 | CONFIGURE CONTROLFILE AUTOBACKUP ON |
| 5 | SBT 库路径找不到 | ORA-27211 | LD_LIBRARY_PATH 包含 $ORACLE_HOME/lib |
| 6 | 恢复时 SCN 不够 | RMAN-06054 |
检查归档日志是否齐全 |
| 7 | Flashback 窗口太短 | 超时无法闪回 | 调大 UNDO_RETENTION + 开启 Guarantee |
| 8 | 跨平台恢复失败 | 字节序不一致 | 只能逻辑迁移(expdp/impdp) |
| 9 | NOARCHIVELOG 模式全备 | 只能恢复到备份时间点 | 生产环境必须 ARCHIVELOG |
| 10 | 恢复演练不做 | 真出事时才发现备份不可用 | 每月恢复演练,验证到数据可读 |
十、小结
Oracle 备份恢复是一门"希望永远用不上但必须时刻准备着"的技术。核心原则三条:
- 多层备份:RMAN 物理备份 + expdp 逻辑导出 + 闪回,三者互补不替代。
- 持续验证:备份成功率监控 + 定期恢复演练 + 坏块检查,缺一不可。
- 异地容灾:通过 SBT 接入 Bareos 等备份系统,把备份复制到独立存储,防止本地存储故障导致备份和数据库一起丢失。
当你第一次在凌晨 3 点接到"数据库挂了"的电话,能在 30 分钟内通过 RMAN 恢复到最近一次归档、数据零丢失地拉起业务时,你就会感谢当初认真配备份策略的自己。

