liuziyi

liuziyi

分享一个Oracle全版本通用归档清理脚本

问题:

归档日志目标(FRA 或 LOG_ARCHIVE_DEST_n 指定目录)空间耗尽后,ARCn 无法继续归档,LGWR 无法完成日志切换,数据库表现为 ORA-00257: archiver error、ORA-19809 / ORA-19804,轻则实例挂起、新连接失败,重则 startup 到 OPEN 阶段失败、数据库起不来。本质是“归档写不进去 → 在线日志不能重用 → 整个实例停摆”,处理的第一目标是尽快释放归档空间让库可用,而不是先去分析归档为什么增长快。

方案:

一、全版本通用归档清理脚本(10gR2 / 11gR2 / 12c / 18c / 19c / 21c / 23ai 通用)

设计要点:自动识别 FRA 与普通归档目录两种模式、按使用率阈值触发、Data Guard 环境强制人工确认、支持试运行、RMAN 清理前先 CROSSCHECK,全部命令在 10g 以后语法一致。

#!/bin/bash
###############################################################################
# ora_arch_clean.sh —— Oracle 归档日志安全清理脚本
# 适用版本:Oracle 10gR2 / 11gR2 / 12c / 18c / 19c / 21c / 23ai
#           单实例、RAC 通用(RAC 只需在任一节点执行一次)
# 用法:
#   ./ora_arch_clean.sh -d 7 -t 80          # 保留7天,使用率超80%才清理
#   ./ora_arch_clean.sh -d 7 -t 80 -n       # 试运行,只列出待删归档
#   ./ora_arch_clean.sh -d 7 -t 80 -s       # 已确认无备库或备库已应用(DG环境必加)
###############################################################################
set -u

RET=7; THR=80; DRY=0; DG_OK=0
while getopts "d:t:nsh" o; do
  case $o in
    d) RET=$OPTARG ;;
    t) THR=$OPTARG ;;
    n) DRY=1 ;;
    s) DG_OK=1 ;;
    *) echo "用法: $0 [-d 保留天数] [-t 使用率阈值] [-n 试运行] [-s 无备库/备库已应用]"; exit 1 ;;
  esac
done

# ---------- 1. 环境变量 ----------
if [ -z "${ORACLE_SID:-}" ]; then
  ORACLE_SID=$(awk -F: '!/^#/ && NF>2 {print $1; exit}' /etc/oratab 2>/dev/null); export ORACLE_SID
fi
if [ -z "${ORACLE_HOME:-}" ]; then
  ORACLE_HOME=$(awk -F: -v s="$ORACLE_SID" '$1==s {print $2}' /etc/oratab 2>/dev/null); export ORACLE_HOME
fi
export PATH="$ORACLE_HOME/bin:$PATH"
export NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'

LOG="$HOME/arch_clean_${ORACLE_SID}_$(date +%Y%m%d).log"
exec >>"$LOG" 2>&1
echo "===== $(date '+%F %T') 开始 SID=$ORACLE_SID 保留=${RET}天 阈值=${THR}% DRY_RUN=$DRY ====="

sqlq() {
  sqlplus -S / as sysdba <<EOF
set heading off feedback off pagesize 0 trimspool on
$1
exit
EOF
}

# ---------- 2. 实例状态检查 ----------
STATUS=$(sqlq "select status from v\$instance;" | tr -d '[:space:]')
case "$STATUS" in
  OPEN|MOUNTED) ;;
  *) echo "[ERROR] 实例状态 ${STATUS:-UNKNOWN},请先启动到 MOUNT 或 OPEN"; exit 2 ;;
esac

# ---------- 3. 识别归档方式并计算使用率 ----------
INFO=$(sqlq "select nvl(max(name),'NONE')||'|'||nvl(round(max(space_used)/nullif(max(space_limit),0)*100,2),0) from v\$recovery_file_dest;")
FRA_NAME=$(echo "$INFO" | cut -d'|' -f1 | tr -d '[:space:]')
FRA_PCT=$(echo  "$INFO" | cut -d'|' -f2 | tr -d '[:space:]')

if [ "$FRA_NAME" != "NONE" ] && [ -n "$FRA_NAME" ]; then
  MODE="FRA"; ARCH_PATH="$FRA_NAME"; PCT="$FRA_PCT"
  echo "[INFO] 归档方式=FRA  路径=$ARCH_PATH  使用率=${PCT}%"
else
  DEST=$(sqlq "select value from v\$parameter where name='log_archive_dest_1';")
  ARCH_PATH=$(echo "$DEST" | sed -n 's/.*LOCATION=\([^ ]*\).*/\1/p')
  [ -z "$ARCH_PATH" ] && { echo "[ERROR] 未识别到 FRA 或 LOG_ARCHIVE_DEST_1 路径"; exit 3; }
  PCT=$(df -P "$ARCH_PATH" | awk 'NR==2 {gsub("%","",$5); print $5}')
  MODE="FILESYSTEM"
  echo "[INFO] 归档方式=文件系统  路径=$ARCH_PATH  使用率=${PCT}%"
fi

if [ "${PCT%.*}" -lt "$THR" ] 2>/dev/null; then
  echo "[SKIP] 使用率 ${PCT}% 低于阈值 ${THR}%"; exit 0
fi

# ---------- 4. Data Guard 保护 ----------
DG=$(sqlq "select count(*) from v\$archive_dest where status='VALID' and destination is not null and dest_id > 1;" | tr -d '[:space:]')
if [ "${DG:-0}" -gt 0 ] && [ "$DG_OK" -ne 1 ]; then
  echo "[ABORT] 检测到备用归档目标,请确认备库已应用归档后加 -s 重跑"; exit 4
fi

# ---------- 5. 试运行 ----------
if [ "$DRY" -eq 1 ]; then
  echo "[DRY-RUN] 以下归档将被删除:"
  sqlplus -S / as sysdba <<EOF
set linesize 220 pagesize 1000
select name, sequence#, first_time, completion_time
from v\$archived_log
where deleted = 'NO' and completion_time < sysdate - $RET
order by completion_time;
exit
EOF
  exit 0
fi

# ---------- 6. RMAN 清理 ----------
rman target / log="$HOME/rman_arch_${ORACLE_SID}_$(date +%Y%m%d).log" <<EOF
CROSSCHECK ARCHIVELOG ALL;
DELETE NOPROMPT EXPIRED ARCHIVELOG ALL;
DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-$RET';
EOF

# ---------- 7. 清理后校验 ----------
echo "[DONE] 清理后使用率:"
if [ "$MODE" = "FRA" ]; then
  sqlq "select 'FRA_USED_PCT='||round(space_used/nullif(space_limit,0)*100,2) from v\$recovery_file_dest;"
else
  df -h "$ARCH_PATH"
fi
echo "===== $(date '+%F %T') 结束 ====="

定时任务(建议每天业务低峰执行一次):

crontab -e
# 每天 02:10 清理,保留 7 天,使用率超过 80% 才执行
10 2 * * * /home/oracle/scripts/ora_arch_clean.sh -d 7 -t 80 >> /home/oracle/arch_clean_cron.log 2>&1

脚本不要放在 $ORACLE_HOME 下,避免升级时被覆盖;同时务必给 crontab 加日志,归档清理脚本“静默失败”是归档被写满的头号原因(例如 SYS 密码过期、环境变量失效、脚本权限丢失)。


二、数据库正常运行时的标准清理步骤

第 1 步:确认归档方式与使用率

-- FRA 模式
SELECT name, space_limit, space_used, space_reclaimable, number_of_files
FROM v$recovery_file_dest;

SELECT file_type, percent_space_used, percent_space_reclaimable, number_of_files
FROM v$flash_recovery_area_usage;

-- 非 FRA 模式:直接看文件系统
-- df -h /arch

第 2 步:确认待删归档与日志切换状态

SELECT name, sequence#, first_time, completion_time, deleted, status, backup_count
FROM v$archived_log
ORDER BY completion_time;

SELECT group#, sequence#, status, archived FROM v$log;

SELECT dest_id, status, destination, error FROM v$archive_dest;

第 3 步:RMAN 清理(先交叉校验,再按时间删)

rman target /
CROSSCHECK ARCHIVELOG ALL;
DELETE NOPROMPT EXPIRED ARCHIVELOG ALL;
DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7';

如需“备份并删除”一体化:

BACKUP ARCHIVELOG ALL DELETE INPUT;

第 4 步:扩容 FRA(保留策略不变、只扩上限时优先用这个)

ALTER SYSTEM SET db_recovery_file_dest_size = 200G SCOPE=BOTH;

第 5 步:验证恢复

ALTER SYSTEM ARCHIVE LOG CURRENT;   -- 强制切一次日志,验证归档链路恢复
SELECT group#, sequence#, status, archived FROM v$log;

三、归档空间满导致数据库起不来的应急恢复

关键原则:先释放归档目标空间,再重试打开数据库。归档空间不足导致打不开时,只要在归档目标处腾出足够空间后重新 ALTER DATABASE OPEN 即可,通常不需要 CLEAR LOGFILE、SWITCH LOGFILE 等额外动作,更不要轻易重建控制文件。

场景 1:FRA 满,实例能到 NOMOUNT/MOUNT,OPEN 失败(ORA-00257 / ORA-19809)

① 先看操作系统层面是否还有空间可写

df -h
tail -200 $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace/alert_$ORACLE_SID.log
# 10g 路径为 $ORACLE_BASE/admin/$ORACLE_SID/bdump/alert_$ORACLE_SID.log

若 FRA 与 $ORACLE_BASE、trace 目录同盘且已 100%,Oracle 连告警日志都写不进去,必须先腾出系统层面空间。

② 扩大 FRA 上限(NOMOUNT 状态即可执行,SCOPE=BOTH 写 SPFILE)

sqlplus / as sysdba
ALTER SYSTEM SET db_recovery_file_dest_size = 200G SCOPE=BOTH;

③ 或把 FRA 迁到空间更大的位置

ALTER SYSTEM SET db_recovery_file_dest = '/newfra' SCOPE=BOTH;

④ 空间实在腾不出来时,在操作系统层转移最老的归档(应急手段)

cd /fra/$ORACLE_SID/archivelog
ls -lrt | head -50          # 按时间从老到新
mv 2026_10_01/* /backup/arch_old/    # 只搬已确认备份过的、最老的归档

⑤ 重试打开

STARTUP MOUNT;
ALTER DATABASE OPEN;

打开后立刻用 RMAN 做一次 CROSSCHECK ARCHIVELOG ALL; DELETE EXPIRED ARCHIVELOG ALL;,把控制文件中已手工移走的记录清理干净。

场景 2:非 FRA(LOG_ARCHIVE_DEST_1='LOCATION=/arch')磁盘满

# ① 把最老的归档转移到其他磁盘(确认已备份)
mv /arch/*.arc /backup/arch_old/

# ② 或直接切换归档目录到新位置(MOUNT 状态可执行)
ALTER SYSTEM SET log_archive_dest_1 = 'LOCATION=/newarch' SCOPE=BOTH;
STARTUP MOUNT;
ALTER DATABASE OPEN;

场景 3:实例还活着但已挂起(ORA-00257,无法切日志)

-- 先用 RMAN 删除过期/最老归档(数据库 OPEN 状态即可执行)
-- DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-2';

-- 空间释放后强制切一次日志,解除 LGWR 阻塞
ALTER SYSTEM ARCHIVE LOG CURRENT;

如果切换仍然失败,再检查 V$ARCHIVE_DEST 的 ERROR 列,确认不是备库/网络/MANDATORY 目标导致的归档卡死(这类情况即使本地空间充足也会报 ORA-00257)。

场景 4:RAC 环境

  • 归档目录通常是共享存储,只需在一个节点执行清理,不要多节点同时跑脚本;
  • 改参数时加 SID='*',保证所有实例一致:
ALTER SYSTEM SET db_recovery_file_dest_size = 200G SCOPE=BOTH SID='*';
  • 只重启单个节点改 FRA 参数,可能出现 ORA-01105 / ORA-19808 参数不匹配,必须所有节点一起重启或手工改一致。

四、注意事项(⚠️ 高风险项)

  1. 先备份、后删除。DELETE ARCHIVELOG 只是删除归档文件,不等于做过备份;没有可用全备 + 归档链时删归档会直接摧毁可恢复性。
  2. 不要用无条件的 DELETE ARCHIVELOG ALL,除非明确确认“已有完整备份且不再需要按时间点恢复”。
  3. 正常状态下禁止手工 rm FRA 目录下的文件,Oracle 推荐统一走 RMAN;只有数据库起不来、RMAN 无法连接时才在 OS 层转移文件,事后必须 CROSSCHECK。
  4. Data Guard 环境:主库删归档前确认备库已应用,建议配置
    CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY;
    否则易报 RMAN-08137,或造成备库归档 GAP 无法追平。
  5. OGG / LogMiner / CDC 抽取进程会持有归档(DBA_CAPTURE),也会触发 RMAN-08137;清理前确认没有正在使用的 LogMiner 会话。
  6. 12c 及以上多租户:归档删除必须在 CDB$ROOT 执行,PDB 内不能单独删归档;按 CON_ID 区分各 PDB 的归档量。
  7. 版本差异:10g 称 Flash Recovery Area,11g 起称 Fast Recovery Area,V$RECOVERY_FILE_DEST、V$FLASH_RECOVERY_AREA_USAGE、DELETE ARCHIVELOG ... COMPLETED BEFORE 在 10gR2 之后语法一致;23ai 同样适用。
  8. 保留天数要 ≥ 最长备份周期 + 1 天;有备库时按备库延迟适当放大。保留 1~2 天属于高风险配置。
  9. 保证还原点(Guaranteed Restore Point)和闪回日志同样占用 FRA,清理前查 V$RESTORE_POINT,必要时先 DROP RESTORE POINT。
  10. 清理脚本本身要被监控:SYS 密码过期、crontab 未生效、环境变量缺失都会让清理“看似在跑、实际没删”,这是归档被写满最常见的隐性根因。建议对“归档使用率”和“清理脚本最近成功时间”双监控。
  11. 清理后必须验证:ALTER SYSTEM ARCHIVE LOG CURRENT; 能成功、V$LOG.ARCHIVED='YES'、告警日志不再出现 ORA-00257,才算真正恢复。
  12. 若 ALTER SYSTEM SET db_recovery_file_dest_size 报无 SPFILE 相关错误,可先用 SCOPE=MEMORY 让库先起来,再 CREATE SPFILE FROM PFILE 固化。

posted on 2026-11-02 09:30  刘子毅  阅读(26)  评论(0)    收藏  举报

导航