数据库运维16 min read

Oracle 19c 备份恢复实战:RMAN 策略设计与 Bareos SBT 集成

一、一次未备份的代价

曾经有个项目的 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 备份恢复是一门"希望永远用不上但必须时刻准备着"的技术。核心原则三条:

  1. 多层备份:RMAN 物理备份 + expdp 逻辑导出 + 闪回,三者互补不替代。
  2. 持续验证:备份成功率监控 + 定期恢复演练 + 坏块检查,缺一不可。
  3. 异地容灾:通过 SBT 接入 Bareos 等备份系统,把备份复制到独立存储,防止本地存储故障导致备份和数据库一起丢失。

当你第一次在凌晨 3 点接到"数据库挂了"的电话,能在 30 分钟内通过 RMAN 恢复到最近一次归档、数据零丢失地拉起业务时,你就会感谢当初认真配备份策略的自己。

分享:

相关文章

评论区