简介:围绕 ORA-00257 归档日志满故障的排查与处置,这份 doc 文档面向 Oracle 数据库管理员和运维人员,适合需要快速恢复实例写入、避免数据抽取任务中断的实战场景。内容以一次完整处理过程为主线,先查看闪回恢复区占用比例与归档日志存放路径,再选择性删除物理归档文件,并通过 RMAN 执行 crosscheck 与 delete expired 释放逻辑空间,同时提示不要一次性清空全部日志,以兼顾数据库可恢复性。文档还说明了删除前确认路径、保留近期日志以及清理后回查空间占用的注意点。压缩包仅含 1 个 doc 文件,大小约 76KB,篇幅紧凑,便于在故障现场按步骤对照操作。目前已有 580 人学习,可作为归档日志满问题的速查手册与操作记录,帮助读者理解空间占用判断、物理清理和 RMAN 交叉校验之间的衔接关系,并积累数据库空间告警的排错思路与处置边界。
1. 凌晨两点的一次抽取停摆:ORA-00257 归档日志满卡在哪一环
凌晨两点,数据抽取任务的调度面板由绿转红,SQL*Plus 里只剩一句ORA-00257: archiver error. Connect internal only, until freed。数据库并没有宕机,是归档日志把闪回恢复区塞满了,Oracle 直接掐断了新的归档写入,所有依赖写入的抽取链路跟着一起停摆。这类 ORA-00257 归档日志满的故障,留给运维的处理窗口通常只有几十分钟,删错一条rm就可能把可恢复性一起删没。这份 ORA-00257 错误处理文档记录的是最小闭环:先看 FRA 占用比例和归档落盘路径,再删物理日志文件,最后进 RMAN 用crosscheck和delete expired把控制文件里的过期记录清掉,让空间真正被回收。它适合日常要顶 Oracle 运维的 DBA、负责抽取调度的后端,也适合手里只有一台库、没人兜底的全栈。
2. 先量后查:把 FRA 占用率和归档落盘路径摸清楚
2.1 为什么不能上来就 rm
闪回恢复区不是一个目录,而是一套由db_recovery_file_dest_size管着的配额。配额之下的flash_recovery_area目录里通常并排放着archivelog/、backupset/、autobackup/,有的库还有onlinelog/里的在线日志镜像。你看到的「归档日志满」,可能是归档文件占了九成,也可能是备份集占了大头,而归档只是压垮配额的那根稻草。两者的处理动作完全不同:前者可以做归档备份后DELETE INPUT,后者得走DELETE OBSOLETE或者干脆扩配额。
还有一种更隐蔽的情况:log_archive_dest_1根本没指向 FRA,而是直接写到一个普通文件系统目录。这时候v$flash_recovery_area_usage看起来很干净,但盘已经 100%,报错一样是 ORA-00257。所以动手前必须先分清「配额满」还是「文件系统满」,这是选型判断,不是可选步骤。
我一般的顺序是:先用数据字典视图看 Oracle 自己怎么算这笔账,再下到操作系统核对实际文件分布,两边对上了才决定删什么。跳过任何一边,都容易删出事故。
2.2 三条 SQL 把占用结构和路径钉死
登录方式上有讲究。文档里用的是sqlplus /nolog再conn /as sysdba,等效于本地 OS 认证,不需要密码,但要求当前 shell 用户属于dba组。生产上我习惯直接sqlplus / as sysdba,少一步交互。
# 切到 oracle 用户,注意中间的短横线:带上它会重新加载 oracle 的环境变量 su - oracle # 免密以 sysdba 身份进入,别用业务账号,业务账号看不到这些视图 sqlplus / as sysdbasu - oracle和su oracle的区别在这类救火场景里很致命:不带短横线不会加载.bash_profile,ORACLE_SID、ORACLE_HOME可能都是空的,sqlplus直接报 command not found,人在着急的时候特别容易在这儿卡几分钟。
进去之后先看配额层面的账:
-- 按文件类型拆分 FRA 占用,file_type 会告诉你到底是 ARCHIVED LOG 还是 BACKUP PIECE 占地方 SELECT file_type, percent_space_used, -- 该类文件占配额的比例 percent_space_reclaimable, -- 其中可回收(已过期/可删)的比例 number_of_files FROM v$flash_recovery_area_usage; -- 配额与实际使用,换算成 GB 更直观;11g 之后这个视图比上面那个更常用 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, number_of_files FROM v$recovery_file_dest;percent_space_reclaimable这个字段值得单独说。它大于 0,说明 Oracle 自己认为有一部分归档已经过了保留策略、可以被清理,但这不代表它会自动删——11g 默认要等你显式执行删除动作,或者等 FRA 压力触发有限度的自动清理。所以看到reclaimable有值而used还顶在 95%,别指望它自己降下来。
接着把路径和归档状态钉死:
-- 归档到底写在哪、配额多大,这两个参数决定了后面所有操作的作用范围 SHOW PARAMETER db_recovery_file_dest; SHOW PARAMETER db_recovery_file_dest_size; SHOW PARAMETER log_archive_dest_1; -- 归档模式、当前归档序列号、最早和最晚的归档,一眼看清断点在哪 ARCHIVE LOG LIST;ARCHIVE LOG LIST的输出里有三行必须看:Database log mode(必须是 Archive Mode,否则整件事不成立)、Automatic archival(ENABLED)、以及最旧在线日志序列号。如果最旧在线日志序列号已经很靠后,说明归档积压时间不短,清理完之后还要确认这段窗口内的备份是否完整。
2.3 v$archived_log:判断哪些能删的唯一依据
物理目录里的文件名只是表象,能不能删要看控制文件里的记录。
-- 只取最近三天,全表扫在归档量大的库上会很慢 SELECT thread#, sequence#, name, completion_time, blocks * block_size / 1024 / 1024 AS size_mb, deleted, -- YES 表示记录已被标记删除,文件不一定还在 status, -- A=可用 D=已删 X=已过期 U=不可用 backed_up -- YES 表示至少有一份 RMAN 备份 FROM v$archived_log WHERE completion_time > SYSDATE - 3 ORDER BY completion_time DESC;deleted和status这两个字段的组合,是排查里最有信息量的东西。deleted=NO但status=X,说明 Oracle 已经比对过文件系统、认定文件不在了,只是记录还没清;deleted=YES而文件还在盘上,说明记录被清但物理文件被别的东西占着(比如被人cp过一份)。这两种状态在救火时都会遇到,看清楚再决定是补crosscheck还是补rm。
2.4 决策分叉:三种占用形态对应三种动作
| 现象 | 判断依据 | 处理动作 |
|---|---|---|
ARCHIVED LOG占用 85% 以上 | v$flash_recovery_area_usage首行占比高 | 归档备份后DELETE INPUT,或按日期删物理文件再crosscheck |
BACKUP PIECE占大头 | 同上,file_type为 BACKUP PIECE 的行占比高 | DELETE NOPROMPT OBSOLETE、缩短保留窗口、必要时扩配额 |
| FRA 视图不满但盘满 | du -sh显示目录已满,v$recovery_file_dest使用率正常 | 归档写在非 FRA 路径,按 OS 层清理并CATALOG重新登记 |
第三行是最容易翻车的一种。有些库在搭建时为了省事,把log_archive_dest_1直接指到/backup/arch,从此 FRA 视图永远显示 0%,监控也就永远不告警,直到某天凌晨磁盘写满。判断方法很简单:SHOW PARAMETER log_archive_dest_1出来的路径如果不在db_recovery_file_dest之下,就属于这种情况。
3. 物理删除到逻辑释放:crosscheck 与 delete expired 的正确顺序
3.1 删了文件空间却不降,是因为账本还挂着
FRA 的使用率不是du出来的,是控制文件里一条条归档记录累加出来的。你在操作系统上rm掉一个 2GB 的归档文件,磁盘腾出 2GB,但控制文件里那条记录还在,space_used一分不减,ORA-00257 该报还报。这就是很多人第一次处理时最迷惑的地方:明明盘空了,数据库还说满。
RMAN 的两个命令正是在补这个差:crosscheck拿控制文件里的记录去文件系统逐个核对,对不上的标记成EXPIRED;delete expired把这些标记过的记录从控制文件里删掉。两步合起来才叫「逻辑释放」,缺一步都不行。文档里的顺序是对的,只是没解释为什么,照着敲容易在中间加戏。
3.2 手工救火的最小闭环
先确认要删的目录结构,别一上来就rm -rf到 FRA 根目录。
# 看一眼 FRA 下有哪些一级目录,确认 archivelog 之外还有没有 backupset ls -l /backup/flash_recovery_area/ # 再看归档目录按日期分了多少天,Oracle 默认按 YYYY_MM_DD 建子目录 ls -l /backup/flash_recovery_area/archivelog/ # 按日期整目录删,保留最近一两天,给正在跑的抽取任务留缓冲 rm -rf /backup/flash_recovery_area/archivelog/2024_06_10 rm -rf /backup/flash_recovery_area/archivelog/2024_06_11 # 删完立刻核对剩余体积,别等数据库报错才发现删错了目录 du -sh /backup/flash_recovery_area/archivelog这里三个参数值得说清楚:-r递归、-f强制不提示,两个合起来就是没有后悔药。所以我习惯先把rm -rf敲成ls -l跑一遍,确认列出来的正是要删的那批,再改回rm。这个习惯救过我至少两次——一次是手滑多打了一层目录,ls直接把backupset的内容刷了一屏。
物理文件删完,进 RMAN 做逻辑释放:
# 用 heredoc 一次喂给 rman,避免交互式敲错;noprompt 让 delete 不再问 YES/NO rman target / <<'EOF' crosscheck archivelog all; delete noprompt expired archivelog all; exit; EOFcrosscheck archivelog all的输出会刷一屏archive log file ... not found,这是预期行为,它的工作就是找出对不上的记录。文档里提到delete expired会提示输入 YES,脚本化场景下加noprompt就免了交互。要注意delete expired只删控制文件记录,不删物理文件——物理文件是上一步rm干掉的,两者分工明确,顺序也不能反:先rm再crosscheck,Oracle 才知道文件真的没了;先crosscheck再rm,记录已经被清,文件反而成了没人管的孤儿。
3.3 更稳的姿势:让 RMAN 自己边备份边删
手工删文件适合应急,长期跑我强烈建议换成 RMAN 一条命令搞定:
rman target / <<'EOF' backup archivelog all format '/backup/rman_bak/arch_%d_%T_%U' delete input; exit; EOFdelete input的语义是:这个归档备份成功落盘之后,把源归档文件删掉,同时清掉控制文件记录。等于一次完成「备份 + 物理删除 + 逻辑释放」三件事,而且不会出现「删了但没备份」的致命状态。format里的%d是库名、%T是日期、%U是唯一编号,三者组合基本能保证不重名。
代价是需要额外的备份空间。如果盘上实在腾不出第二份,那就只能在「先删后补备份」和「先扩盘再备份」之间选,我一般选后者——扩一块盘的成本,远低于一次恢复不了的故障。
3.4 删完怎么验证真的生效
别看一眼命令没报错就收工,三个地方都确认一遍:
-- 使用率应该明显下降;如果没降,说明还有别的 file_type 占着 SELECT file_type, percent_space_used, number_of_files FROM v$flash_recovery_area_usage; -- 总量核对,reclaimable 应该也跟着降下来 SELECT space_limit/1024/1024/1024 AS limit_gb, space_used /1024/1024/1024 AS used_gb, space_reclaimable/1024/1024/1024 AS reclaimable_gb FROM v$recovery_file_dest;再加一条 RMAN 侧的检查:
rman target / <<'EOF' list expired archivelog all; exit; EOF输出no expired archive logs found才算干净。如果还能列出一堆,说明delete expired那步漏了或者执行失败,得重新走一遍。
3.5 控制文件记录保留期:一个隐形的天花板
这条坑非常隐蔽,我踩过一次之后每次都会先查。控制文件里放归档记录的区域是循环复用的,保留时长由control_file_record_keep_time控制,默认 7 天。也就是说,超过 7 天的归档记录可能已经被新记录覆盖掉了。
后果是:你rm掉一批 10 天前的归档,crosscheck根本看不到它们(记录都没了,拿什么比对),delete expired也就什么都不会删,空间照样不降,而du又显示盘是空的。这时候人会陷入「明明删了却没用」的自我怀疑。
-- 默认 7 天,建议调到不小于归档保留天数 + 7 SHOW PARAMETER control_file_record_keep_time; ALTER SYSTEM SET control_file_record_keep_time = 31 SCOPE = BOTH;调这个参数会占用更多控制文件空间,如果控制文件是REUSE且没有多路复用,改完记得ALTER DATABASE BACKUP CONTROLFILE TO TRACE留一份创建脚本,方便以后重建。另一种兜底手段是用CATALOG START WITH '/backup/flash_recovery_area/archivelog'把文件重新登记回控制文件——但那是「文件还在、想找回来」的场景,跟我们想删的方向正好相反,别用错。
4. 从一次删除到长期可控:归档备份与保留策略落地
4.1 先定 RPO,再定保留天数
保留几天归档,不该拍脑袋。它由两个数决定:业务能接受丢失多少数据(RPO),以及一次完整恢复要多久(RTO)。如果业务能接受回退 4 小时,那归档保留 3 天配每日全备就够了;如果要求能回到任意时间点,那闪回和归档的保留周期得按周算。
我见过最常见的错误配置是:归档保留 7 天,但全备一周才做一次,两个窗口一错位,最坏情况下恢复出来的库缺一天数据,而且没人知道。定参数之前先把这个时间轴画出来,比什么优化都值。
4.2 RMAN 侧的三条配置
rman target / <<'EOF' -- 恢复窗口 7 天:RMAN 认为 7 天内可恢复所需的备份都不能删 CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS; -- 归档删除策略:只有成功备份过 1 份到磁盘的归档才允许被删 CONFIGURE ARCHIVELOG DELETION POLICY TO BACKED UP 1 TIMES TO DEVICE TYPE DISK; -- 控制文件自动备份:结构变更后自动留一份,恢复时省大事 CONFIGURE CONTROLFILE AUTOBACKUP ON; EOF第二条是整段的重点。它相当于给所有删除动作上了一把锁:没备份过的归档,DELETE INPUT也删不掉。这就从机制上堵死了「手快删了没备份的归档,结果恢复链断掉」这条路。代价是如果备份一直失败,归档会一直堆积,所以必须配套监控备份作业本身——这就引出了下面第 5 章的坑。
4.3 每天跑的归档备份脚本
#!/bin/bash # /home/oracle/bin/bak_arch.sh —— 归档备份 + 清理,放在业务低峰执行 export ORACLE_SID=orcl export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 export PATH=$ORACLE_HOME/bin:$PATH LOG=/home/oracle/bin/log/bak_arch_$(date +%Y%m%d).log rman target / log=$LOG <<'EOF' RUN { ALLOCATE CHANNEL c1 DEVICE TYPE DISK; BACKUP ARCHIVELOG ALL FILENAME '/backup/rman_bak/arch_%d_%T_%U' DELETE INPUT; BACKUP CURRENT CONTROLFILE FORMAT '/backup/rman_bak/ctl_%d_%T_%U'; DELETE NOPROMPT OBSOLETE; RELEASE CHANNEL c1; } EXIT; EOF # 退出码非 0 就往告警文件里写一行,别让失败静默过去 if [ $? -ne 0 ]; then echo "[$(date '+%F %T')] rman backup failed, see $LOG" >> /home/oracle/bin/log/alert.log fi脚本里有几个细节:log=$LOG把 RMAN 全量输出落到文件,出问题时有据可查;显式export三个环境变量,是因为crontab不加载.bash_profile,不写就是空值;ALLOCATE CHANNEL显式声明通道,方便以后加并行度(ALLOCATE CHANNEL c2 ...);BACKUP CURRENT CONTROLFILE单独做一份,和控制文件自动备份互为补充。
DELETE NOPROMPT OBSOLETE放在最后,按保留策略清掉不再需要的备份集。注意它清的是备份,不是归档,别和DELETE INPUT搞混。
4.4 四个关键参数的取值参考
| 参数 | 建议取值 | 说明 |
|---|---|---|
db_recovery_file_dest_size | 日归档量 × 保留天数 × 1.5 | 留 50% 余量吸收批量作业的归档峰值 |
db_recovery_file_dest | 独立挂载点,不与应用日志、数据文件同盘 | 同盘时归档写满会连带影响数据文件写入 |
control_file_record_keep_time | ≥ 归档保留天数 + 7 | 太短会让老归档记录被覆盖,crosscheck失效 |
log_archive_dest_1 | 指向 FRA 之内 | 指到外面 FRA 视图统计不到,监控会瞎 |
调整配额用一条命令,注意SCOPE=BOTH:
-- 调大可以随时做;调小要谨慎,当前使用量超过新配额会立刻开始报 ORA-00257 ALTER SYSTEM SET db_recovery_file_dest_size = '160G' SCOPE = BOTH;4.5 挂进 crontab
crontab -e # 每天 02:30 备份归档,和全备错开至少两小时,避免 IO 打架 30 2 * * * /home/oracle/bin/bak_arch.sh > /dev/null 2>&1脚本本身已经把输出重定向到日志文件,这里的> /dev/null只是防止crontab因为 stdout 发送失败而产生退信。上线第一周建议把2>&1改成指向一个临时文件,确认没有环境变量类报错之后再关掉。
5. 避坑与排查:五条踩过的坑
5.1 现象:rm 完,使用率一点没降
原因:只删了物理文件,控制文件里的归档记录还在,space_used是记账算出来的,不认du。解决:按「crosscheck archivelog all→delete noprompt expired archivelog all」补一遍,然后回查v$recovery_file_dest。如果补完还不降,查control_file_record_keep_time是否太短导致记录已被覆盖,这时候只能靠CATALOG重新登记再删,或者直接扩配额先顶住。
5.2 现象:crosscheck 刷出一屏 not found,以为出大事了
原因:这是crosscheck的正常输出,它就是在报告「控制文件有、文件系统没有」。真正该紧张的是另一种情况——crosscheck跑完静悄悄什么都不报,delete expired还提示没有过期归档,但盘上明明少了一批文件。那说明记录早被覆盖,Oracle 压根不知道这批文件存在过。解决:先SELECT COUNT(*) FROM v$archived_log WHERE deleted='NO'和目录里的文件数对一下,数量差得多就是记录丢失,处理顺序是先调大control_file_record_keep_time,再考虑扩配额或加盘,别指望 RMAN 帮你找回记忆。
5.3 现象:delete expired 执行完,FRA 还是 99%
原因:占地方的不是归档。FRA 里backupset/和autobackup/长期累积,尤其是控制文件自动备份每次结构变更都生成一份,几个月下来能占几十 G。解决:先按file_type分组看占用分布,确认是备份占大头之后走DELETE NOPROMPT OBSOLETE,配合CONFIGURE RETENTION POLICY收窄保留窗口。不要直接rm备份集目录——那会让 RMAN 目录和物理文件彻底脱节,比归档的问题难收拾得多。
5.4 现象:清理完第二天又满了
原因:要么归档生成速度超过清理速度(大批量导入、索引重建、日志切换过频),要么备份脚本一直在失败但没人发现,ARCHIVELOG DELETION POLICY把删除动作全挡住了。解决:先查备份作业历史SELECT * FROM v$rman_backup_job_details ORDER BY start_time DESC,确认最近几天是否有成功记录;再看归档生成速率SELECT TO_CHAR(completion_time,'YYYY-MM-DD HH24') h, COUNT(*) FROM v$archived_log GROUP BY ...,找出峰值时段。速率问题靠清理解决不了,得从日志切换频率(ARCHIVE_LAG_TARGET)、批量作业拆分、归档压缩三个方向动。
5.5 现象:清理之后做恢复演练,报归档缺失
原因:删掉了一批还没进备份、或者备库还没应用的归档。单机环境顶多恢复不到某个时间点,有备库的环境会直接出现归档 Gap,备库追不上主库。解决:删之前一定要看backed_up字段,只删backed_up=YES的;有备库的环境还要确认applied状态。这类操作没法回滚,唯一的后悔药是确认无误再动手。养成「先SELECT列出待删清单,肉眼过一遍,再执行删除」的习惯,比任何脚本都可靠。
排查顺序上,我一般按「配额层 → 目录层 → 记录层」三级往下走:先看v$recovery_file_dest确认是不是真满了,再du看是哪个子目录占的,最后查v$archived_log对账。三级里任何一级跳过,都可能把简单问题做复杂。
6. 把救火变成监控:一条阈值 SQL 加一份验证清单
救火能力再强,也不如提前半小时知道。归档空间的监控其实一条 SQL 就够,关键是放对地方、跑对频率。
-- 监控平台每 5 分钟跑一次,返回值 > 85 就告警,> 95 直接电话 SELECT ROUND(space_used / space_limit * 100, 2) AS used_pct, ROUND(space_reclaimable / space_limit * 100, 2) AS reclaimable_pct FROM v$recovery_file_dest;这两个数要一起看。used_pct高但reclaimable_pct也高,说明有得救,跑一次归档备份就能下来;两个都低但used_pct高,那是备份集在占地方,得动保留策略;used_pct高而reclaimable_pct是 0,往往意味着归档一直没备份成功,这时候该查的是备份作业,不是空间。
阈值别一刀切定 90。我一般按归档日增量算:如果一天涨 8%,85% 告警意味着还有将近两天缓冲;如果一天涨 30%,85% 告警基本等于「两小时后就要出事」,得把阈值压到 70 并缩短采集间隔。
每次动完归档,我还会走一遍下面这份清单,五分钟内能全过才算收工:
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| 空间是否真降 | SELECT space_used FROM v$recovery_file_dest; | 降幅与删除量基本吻合 |
| 是否还有过期记录 | list expired archivelog all; | 提示 no expired archive logs found |
| 归档链是否连续 | SELECT thread#, MIN(sequence#), MAX(sequence#) FROM v$archived_log GROUP BY thread#; | 无缺口 |
| 备份是否成功 | SELECT status FROM v$rman_backup_job_details ORDER BY start_time DESC FETCH FIRST 1 ROWS ONLY; | 返回 COMPLETED |
| 监控是否生效 | 手工把阈值调低跑一次 | 能正常触发告警 |
最后一行经常被忽略。监控配好了不代表能用,我见过告警规则写对了、但采集账号没有SELECT ANY DICTIONARY权限,跑了一个月全是空值,真出事那天一点声音都没有。
血泪经验是:所有跟归档有关的操作,我都不再相信「命令没报错 = 成功」。rm不报错不代表删对了目录,crosscheck不报错不代表记录还在,delete expired不报错也不代表空间降了。真正可靠的只有回查那两条视图 SQL。从那以后我每次清理归档,不管多急,都强制把「删前SELECT列表、删后回查使用率、最后跑一遍list expired」走完整套,宁可多花三分钟,也不想再经历一次盘空了却还报 ORA-00257 的自我怀疑。希望帮到你。
本文还有配套的精品资源,点击获取