Oracle归档日志清理实战:用RMAN安全释放FRA空间 接手过不少Oracle数据库的人大概率都经历过这样的早晨登录系统一看告警邮件躺了一堆数据库会话全部卡在“ARCH wait”上应用那边已经在报连接超时。点开告警详情果不其然——快速恢复区FRA使用率直逼100%归档日志占满了磁盘。这类事故在Oracle运维里太典型了但凡归档模式下跑一段时间日志清理就是个绕不开的话题。今天就把归档日志清理这件事儿从头捋一遍重点是配合RMAN怎么安全地清、高效地清以及过程中那些容易踩的坑。这篇内容适合刚接手数据库运维的DBA、自己管服务器的小团队也适合准备系统梳理一遍备份策略的技术负责人。不需要太深的基础能看懂命令、理解思路就行。1. 归档日志是什么为什么它总是惹麻烦1.1 归档日志的由来和真实作用Oracle数据库有一个机制叫Redo Log也就是在线重做日志记录所有数据变更操作。它的文件是固定几个、循环复用的写满一组就切到下一组。在开启归档模式ARCHIVELOG Mode之后日志切换时后台进程会把写满的那一组在线日志完整拷贝一份存成“归档日志”Archive Log。这部分日志是连续编号、带序列号的形成一条完整的历史变更链路。很多人一开始不理解为什么要多此一举存这么多日志直接复用覆盖不好吗在非归档模式下确实是不存崩溃恢复在绝大多数场景也够用。但一旦涉及“时间点恢复”或者“备库同步”没有归档日志基本寸步难行。比如误删了一张表想用RMAN恢复到删表之前那一刻备库想追上主库的变更靠的都是归档日志。所以归档日志的本质就是数据库层面的“历史录像带”而且是不能随便减掉的那一种。1.2 不清理的话会发生什么日志只增不减磁盘迟早会被打满。FRA满掉的后果比大多数人想得严重此时数据库的任何提交动作都无法完成因为每产生一次日志切换都要写新的归档而FRA没有空间数据库会直接挂起所有业务会话停在日志写入这一步。想通过重启解决是不可能的——启动过程中同样需要写归档启动一样会卡住。还有一种隐蔽的情况归档日志本身没有占满FRA而是存放归档的独立目录满了。有些环境为了性能把二进制的归档日志单独放在一块盘上不去管FRA空间。这种情况下数据库同样会挂而且FRA使用率曲线看起来还一切正常排查起来更费劲。所以不管归档放在哪清理都是一个强需求差别只是埋在哪个环节里。1.3 清理工作的本质矛盾清理归档日志这件事表面看是“删文件释放空间”实际上是在“恢复能力”和“磁盘空间”之间做权衡。归档日志从诞生起就承担着好几个职责本机崩溃恢复需要它基于时间点的恢复需要它备库同步需要它甚至CDC同步、外部订阅也时不时会来读一段。一股脑全删省了空间但丢了“后悔药”一条不删磁盘很快就报警。所以“清理”不是简单敲一条删除命令而是要搞清楚哪些日志可以被安全删除哪些还需要保留删除之后RMAN和数据库的状态是否一致。带着这个问题去看下面的方案思路就清晰多了。2. 为什么清理推荐用RMAN而不是直接删文件2.1 直接删文件有什么隐患有些刚入门的朋友习惯性用操作系统命令直接删除归档日志文件比如rm -f报错路径下的*.arc觉得只要磁盘空间释放了就算完事。这样的做法在短期应急时确实能腾出空间但隐患特别多。最直接的问题是控制文件不一致。Oracle控制文件里记录着每一个归档日志的元数据信息包括名字、序列号、时间戳、是否参与备份等。你用rm直接删掉文件控制文件里没有这个标记之后执行RMAN备份或者恢复操作时RMAN会去校验这些日志是否还存在然后报出一堆错误。正常流程里crosscheck和备份都会因为找不到文件而中断严重的时候还会影响恢复路径的判断。另一个问题是备份策略失效。很多人的备份脚本都是“先备份归档日志再删除已备份的归档日志”。如果手动删了文件RMAN检查时发现一批归档日志缺失就无法确认哪些已经备份过、哪些没有备份日志的环节会变得非常混乱甚至可能因为这个原因丢掉应该有备份的日志真到恢复的时候才发现要用的归档早就没了。2.2 RMAN删除时到底做了什么事RMAN删除归档日志并不是简单执行一条系统调用删除文件它的处理逻辑远比rm要严谨。执行delete archivelog时RMAN会同时更新控制文件中的元数据把删除动作记录到RMAN的恢复目录如果配置了或控制文件里这样数据库内部不会残留无效的记录。更关键的是RMAN会依据自身记录的备份情况来判断这次删除是否会破坏恢复链。比如加上了backed up 1 times to device type disk条件RMAN会检查每条归档日志是否至少有一个冗余备份确认存在才允许删除。这条保护逻辑是直接删除文件完全不具备的。再说跨平台操作。直接删文件在单机Linux环境可能够了但生产环境很可能是多节点RAC或者主备架构。每台机器的归档日志产生情况不一致用脚本来删很容易漏删、错删。RMAN工具天然理解实例和节点的归档日志分布对于RAC环境也能正确管理所有实例产生的归档日志这个优势在实际运维中非常明显。2.3 什么时候可以“破例”直接删除虽然是推荐用RMAN但有些场景下直接删除是可以接受的比如FRA已经100%满、数据库处于挂起状态、需要紧急抢救可用性的时候。这种时候丢失一点历史归档带来的风险远小于数据库不可用的影响先用rm把最旧的一批日志删掉让系统活过来后续再用RMAN做一遍交叉校验同步状态。切记紧急处理完之后一定补一次crosscheck和清理避免状态不一致持续下去。3. RMAN清理归档日志的完整实操3.1 清理前必须做好的状态检查任何生产操作前先摸清现状。这一步是做清理的地基状态没搞清楚就动手删日志后面很容易翻车。先看FRA空间使用情况SELECT name, total_size / 1024 / 1024 / 1024 AS total_gb, used_size / 1024 / 1024 / 1024 AS used_gb, space_limit / 1024 / 1024 / 1024 AS limit_gb, space_used / 1024 / 1024 / 1024 AS used_in_limit_gb, space_reclaimable / 1024 / 1024 / 1024 AS reclaimable_gb FROM v$recovery_file_dest;再看归档日志的总量和分布情况SELECT ROUND(SUM(blocks * block_size) / 1024 / 1024 / 1024, 2) AS total_archive_gb, COUNT(*) AS archive_count, MIN(first_time) AS oldest_time, MAX(first_time) AS newest_time FROM v$archived_log WHERE archived YES AND deleted NO;顺手查一下每日归档生成量这个数据对确定保留周期很关键SELECT TO_CHAR(first_time, YYYY-MM-DD) AS day, COUNT(*) AS cnt, ROUND(SUM(blocks * block_size) / 1024 / 1024 / 1024, 2) AS size_gb FROM v$archived_log WHERE first_time SYSDATE - 14 GROUP BY TO_CHAR(first_time, YYYY-MM-DD) ORDER BY day;这些查询跑一遍心里就有数了归档日志每天大概产生多少、最多能保留几天、FRA什么时候会遇到压力。我第一次做清理的时候没看生成量直接设了个“保留七天”的策略结果一个业务大促日生成了200多GB归档磁盘差点又爆后来才学会先把日生成峰值查出来再定策略。3.2 最常用的删除命令逐个解析RMAN里删归档日志的命令语法上围绕delete archivelog展开但限定条件不同效果差别很大。下面这几个是最常用的。删除某个时间点之前的所有归档日志这是最常规的操作rman target / EOF DELETE NOPROMPT ARCHIVELOG UNTIL TIME SYSDATE-7; EOF这条命令会把7天前的所有归档日志全部删除。注意这里的时间是SYSDATE-7也就是当前时间往前推7天归档日志里记录的是first_time和completion_timeRMAN对比的是哪个字段需要看版本和实际行为建议理解成“这个时间点之前完成的所有归档日志都会被删”。如果你希望严格从某个时间点开始全部保留也可以用UNTIL TIME TO_DATE(2025-01-01 00:00:00,YYYY-MM-DD HH24:MI:SS)这种写法但生产上很少用特殊时间点基本都是相对时间。删除已经备份过的归档日志这个策略最推荐rman target / EOF DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK; EOF这里的BACKED UP 1 TIMES TO DEVICE TYPE DISK表示只删除那些“已经在磁盘上备份过至少一次”的归档日志。换句话说每一条归档日志都已经有一个备份删掉原始文件之后如果需要恢复还能用备份来还原安全性大大提高。这条命令我基本每周跑一次日常归档清理的主力就是它。还有一种按sequence删除的方式rman target / EOF DELETE NOPROMPT ARCHIVELOG UNTIL SEQUENCE 13250; EOF这条会删除序列号小于等于13250的所有归档日志。sequence是数据库日志的唯一递增序列号按它来删的好处是非常明确、不会误删新产生的日志。缺点是DBA得先知道当前日志序列号到哪里了查询方式SELECT MAX(sequence#) FROM v$archived_log;3.3 删除后的交叉校验和空间确认删除完并不代表工作结束。RMAN在删除过程中会维护状态但外部因素比如之前有手动删过的日志可能导致记录不准确。删除之后建议跑一次交叉校验让RMAN核对物理文件和控制文件记录是否一致rman target / EOF CROSSCHECK ARCHIVELOG ALL; DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; EOF交叉校验会把控制文件里记录的每个归档日志去文件系统里实际看一眼存在与否都给标记。标记为EXPIRED的就是文件已经不在了但控制文件还有记录的后续删除命令会把它们统一清掉保持内部状态干净。然后再确认一下空间是否真正释放SELECT ROUND(space_used / 1024 / 1024 / 1024, 2) AS used_gb, ROUND(space_reclaimable / 1024 / 1024 / 1024, 2) AS reclaimable_gb FROM v$recovery_file_dest;如果用的是独立目录存放归档日志而不是FRA那么可以用ls -lh或df -h直接看目录占用变化。释放不及时的原因一般有两个一是RMAN删除的是控制文件记录和文件句柄但某些进程还占用着文件比如DG传输进程正在读取二是文件系统层面空间回收有延迟等几秒再查就正常了。3.4 快速清理到指定安全水位的一键脚本生产环境里有时候不是一个星期跑一次清理而是磁盘使用率超过阈值就要自动清理比如FRA超过85%就触发。这种场景下面这个脚本很实用#!/bin/bash # archive_cleanup.sh - RMAN archive log cleanup script export ORACLE_SIDorcl export ORACLE_HOME/u01/app/oracle/product/19.0.0/dbhome_1 export PATH$ORACLE_HOME/bin:$PATH LOG/home/oracle/logs/archive_cleanup_$(date %Y%m%d%H%M).log rman target / log$LOG EOF CROSSCHECK ARCHIVELOG ALL; DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK; LIST ARCHIVELOG ALL; EOF exit 0脚本放到crontab里每天凌晨执行一次能把归档日志始终控制在较低水位。个人建议不要用“保留N天”的硬策略作为唯一手段因为业务会有高峰低谷硬策略在高峰日可能把几天前的日志也删掉但备份还没完成。BACKED UP 1 TIMES的写法能把“是否安全删除”的判断交给RMAN综合了时间和备份两个维度的约束更稳妥。4. 监控预警和保留策略怎么定4.1 归档日志该保留多久这个问题的标准答案永远是“看需求”但每个需求都要落到具体指标上。常见的保留需求有满足介质恢复的时间窗口、满足备库同步延迟、满足审计合规要求、满足CDC类外部订阅。对这几种情况做一个简单的优先级排序如果备份策略是每天一个全备加每日归档备份那么只要保留最近一天到两天的本地归档就能满足常规恢复需求。如果有备库就要看备库同步的延迟正常情况下备库会实时接收归档延迟最多几分钟保留一天的归档足够但如果备库停机维护两天回来后需要从主库拉取缺失的归档日志这时候保留窗口就得拉长到停机时长加上余量。我一般建议这样设计算好每天归档生成量统计出峰值再用“保留时间×日均/峰值生成量安全余量”来配置FRA大小。假设每天生成60GB归档想保留7天那FRA里归档相关空间至少得预留420GB再加上全备文件和其他文件整个FRA空间建议给到800GB以上。实际项目里FRA只给几百GB但归档每天上百GB的情况不少见这种配置根本撑不住一周的归档策略就要相应调整。4.2 利用告警机制提前发现风险清理做得再好也不如监控提前发现问题。Oracle提供了现成的视图和告警机制来跟踪FRA空间。最直接的一条SQL每小时跑一次SELECT ROUND((space_used / space_limit) * 100, 2) AS pct_used FROM v$recovery_file_dest;配合邮件或企微通知脚本FRA使用率超过85%就警告超过92%就执行清理脚本超过97%就直接紧急删除。告警阈值不要设成90%才开始动作因为倒腾日志、释放空间都需要时间等到90%再处理业务高峰一冲就到100%了连紧急操作的空间都没有。归档日志目录的磁盘空间监控也不要只看FRA。用独立目录放归档的时候把目录所在文件系统的使用率也加进监控脚本里同步设置阈值双保险。我们线上就出过一次FRA使用率视图正常但独立归档盘的inode用完了数据库一样卡住DBA排查了好久才发现问题根源。4.3 定时清理任务设计建议定时清理任务设计上有一个容易忽略的点清理时间段应该和备份窗口、业务高峰期错开。最好是在全量备份完成之后、业务低峰期执行。典型的场景是凌晨2点跑全备凌晨5点跑归档清理这样既保证备份完成后归档日志有冗余又不影响白天数据库性能。清理频率方面归档生成量不大的系统可以一天一次生成量大的系统建议一天两到三次或者做成分级方案高频小批量清理最近产生的归档低频大批量清理过期归档。举个例子每天6点和18点各跑一次“删除已备份的归档日志”同时每天凌晨3点跑一次“删除7天前的全部归档日志”两者配合FRA空间使用率会很平稳。4.4 区分备份策略disk还是sbtRMAN备份目的地直接影响清理命令的写法和逻辑。如果备份走的是磁盘目录DELETE语句里写的是TO DEVICE TYPE DISK。这个条件下BACKED UP 1 TIMES意味着归档日志已经被备份到磁盘删除原始归档后还原时可以从这些备份文件恢复逻辑自洽。如果备份走的是磁带或第三方存储设备类型要对应替换比如TO DEVICE TYPE sbt。很多第三方备份软件在delete input的时候会联动删除对应的备份文件RMAN不一定感知。这种情况下更要小心确认备份软件确实完成了归档备份动作再删除原始归档。我见过某套系统用第三方备份工具备份归档日志脚本里单纯DELETE ARCHIVELOG ALL结果把还没被备份的日志删了后来恢复时少了一段日志只能靠不完全恢复弥补教训挺深刻。5. 常见问题和排查思路实录5.1 归档日志删了但空间没变化这个问题的频率非常高。执行完DELETE ARCHIVELOG之后v$recovery_file_dest里space_used没有明显下降甚至偶尔还上升了。原因有这么几个一是FRA里除了归档日志还有其他文件比如数据文件备份、控制文件备份归档日志删掉的份额小于新增文件的占比二是RMAN删除的是归档记录实际文件可能还被进程占用特别是正在向备库传输的归档日志删除后文件句柄未释放三是你的环境和配置可能启用了DB_FLASHBACK_RETENTION_TARGET闪回日志也在占用FRA空间。排查的时候先用这个查询看看FRA里各类文件的占比SELECT file_type, ROUND(percent_space_used, 2) AS pct_used, ROUND(percent_space_reclaimable, 2) AS pct_reclaimable FROM v$recovery_area_usage;如果ARCHIVED LOG对应的占比已经很低空间还是满的就说明主要是别的文件占着需要针对性处理比如清理过期的备份集。如果归档日志占比很高但删除后未释放那就用lsof L1Linux查一下哪些进程还在持有已删除的文件句柄锁定了进程再决定重启服务还是等待传输完成。5.2 RMAN报错RMAN-08137: archived log not deleted这个报错在清理归档时很常见含义是“有一条归档日志未被删除因为它不在归档日志备份策略的范围内”。说白了就是你给RMAN下达了DELETE ARCHIVELOG ALL BACKED UP 1 TIMES但某条日志在RMAN的元数据里没有被标记为“已备份”RMAN出于安全考虑拒绝删除。解决办法是调整删除策略或者先确保所有待删归档都已经完成备份。可以这样查询没被备份过的归档日志SELECT sequence#, first_time, backed_up FROM v$archived_log WHERE archived YES AND deleted NO AND backed_up NO;如果确认这些日志不重要或已经不需要了可以把删除条件改宽比如直接从DELETE NOPROMPT ARCHIVELOG UNTIL TIME SYSDATE-7;来删。如果日志需要保留但RMAN还没备份它们那就先跑一次BACKUP ARCHIVELOG ALL DELETE INPUT;把备份和清理一起完成。注意这个命令的含义是备份所有未备份的归档日志成功后立即删除原始文件一步到位省事但需要确认备份目的地空间够用。5.3 DG备库追不上日志归档被提前删了主备环境下备库需要从主库拉取归档日志来恢复数据。如果主库上的归档日志被过早删除备库还没来得及拉取就会导致备库中断后面只能通过重建备库或手动补齐日志的方式恢复。这种情况比磁盘满还难受因为故障恢复周期特别长。预防措施有两点。一是保留窗口要覆盖备库中断时间的最大值。比如备库可能停机三天那主库归档日志保留窗口至少要在备份保留窗口上再加三天。二是把备库拉取情况和归档清理联动只有在备库确认已接收并应用了某条归档日志之后主库才允许删除这条日志。具体可以通过查询v$archive_dest_status里备库的status来辅助判断。SELECT dest_name, status, error, redo_log_count FROM v$archive_dest_status WHERE dest_name IN (standby_log_archive, LOG_ARCHIVE_DEST_2);如果状态不是VALID说明备库接收可能存在问题此时清理操作就需要谨慎最好暂停自动清理先排查备库链路。5.4 手动删除过归档日志导致crosscheck一堆expired前面提到过手动删文件会让控制文件里留存一堆无效记录。最好的处理方式是让RMAN感知现实用交叉校验把状态修正。操作不复杂但顺序有讲究rman target / EOF CROSSCHECK ARCHIVELOG ALL; DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; EOF先交叉校验再删除过期记录两步执行完控制文件和物理文件的状态就恢复了。不要省略交叉校验直接删除那样RMAN可能会因为找不到文件而另行报错。如果归档日志特别多交叉校验全量记录会比较慢可以按时间范围或序列号做局部校验比如CROSSCHECK ARCHIVELOG UNTIL TIME SYSDATE-7;。5.5 清理后恢复点失效怎么办还有一种容易被忽视的问题某些恢复操作依赖特定的归档日志集合如果你删除了某个时间点的归档刚好那一段是某次不完全恢复的必经之路恢复就可能失败。比如你要恢复到昨天上午10点但昨天上午9点之前的部分归档已经被清理掉了那么恢复就无法精确到10点只能选择更早的恢复点或者忍受数据丢失。要避免这种问题设计保留窗口时一定要参考你可能的恢复需求。最典型的是如果你有“恢复到两天前某个时刻”的需求却把归档清理策略设为保留一天那这个恢复需求就永远不可用。一般建议恢复需求和归档保留时间联动设置恢复需求最长回溯多少天归档至少保留多少天再在这个基础上加备份冗余天数形成最终保留窗口。6. 一些实操心得和小技巧6.1 干脏活之前先演练删除归档日志这类操作看着简单但在正式环境执行前建议找一个测试库先演练一遍。测试环境里把FRA空间设置小一点模拟归档满的情况把交叉校验、删除过期记录、删除已备份记录、空间确认这一套完整跑一遍。RMAN的命令语法在不同版本之间有细微差别演练能提前发现这些坑。比如有些旧版本里DELETE ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK的写法就不完全兼容必须在你的目标版本上验证过再上生产。6.2 保留一个实时的清理日志每跑一次归档清理脚本把输出内容存到本地日志文件。脚本里的log$LOG参数就是干这个的。清理日志积累起来之后有大用途可以分析归档生成的趋势、判断清理策略是否需要调整、排查历史问题时回溯前因后果。我在每个库上都保留了至少三个月的清理日志定位过一次“归档日志莫名增多”的问题就是从日志里发现特定时间段生成量突然增大进一步排查定位到业务侧批量任务效率很高。6.3 关键时刻不要忘了flashback如果数据库开启了闪回数据库功能FRA空间里还会存放闪回日志闪回日志同样会占用FRA空间但和归档日志的清理没有直接关系。闪回日志由Oracle自动管理过期自动清理。动手删归档日志之前记得区分一下当前占空间的到底是归档还是闪回日志别误删了归档才发现闪回日志才是大头那就尴尬了。用V$FLASHBACK_DATABASE_LOG可以查看闪回日志的估算空间需求和ESTIMATED_FLASHBACK_SIZE提前做到心中有数。6.4 归档日志清理尽量标准化把归档清理做成标准操作文档内容包含状态检查SQL、RMAN删除命令、删除后验证SQL、告警阈值说明、常见报错对照。每个库执行时都按文档来不搞个人随机发挥。尤其在一个团队多个人管理多个数据库的场景下标准化的操作流程能避免很多意外。我在团队里推行这套文档后归档日志相关的事故率明显下降新人接手也能照着规范独立操作不用每次找老人问一遍。6.5 清理脚本里加上自我保护开关给自动清理脚本加一个“安全开关”核心逻辑是只有当FRA使用率确实超过阈值时才执行清理低于阈值就直接结束。这样可以避免因为脚本误判或者计划任务重复执行导致归档日志被过早清掉。示例脚本pct_used$(sqlplus -s /nolog EOF set pages 0 set feedback off connect / as sysdba SELECT ROUND((space_used / space_limit) * 100, 2) FROM v\$recovery_file_dest; EOF ) pct_used$(echo $pct_used | tr -d ) if [ $(echo $pct_used 80 | bc) -eq 1 ]; then rman target / EOF CROSSCHECK ARCHIVELOG ALL; DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK; EOF else echo FRA usage $pct_used%, no cleanup needed fi类似的逻辑还能扩展成“如果备库状态异常跳过清理”这个保护对主备环境尤其重要。收尾我自己踩过坑之后的几条坚持归档日志清理看起来是个体力活实际上每一次清理都是在和“数据可用性”做交易交易做不好早晚在恢复的时候还债。这么多年运维下来我给自己定了三条铁律第一任何清理操作都基于RMAN绝不图省事直接删文件第二清理策略必须跟随业务恢复需求和备库状态动态调整一条SQL用一年不动的策略基本都出过问题第三每一次清理操作必须留日志、留记录、可回溯。这三条坚持下来归档日志这块的夜班电话确实少了很多。最后再分享一个小技巧把你的RMAN命令写成一个统一的脚本文件每次执行前先查一次FRA使用率再决定是跑DELETE INPUT还是DELETE ALL BACKED UP 1 TIMES这套组合拳实测下来既安全又灵活尤其在那些归档生成量波动大的系统上比固定一个删除指令好使得多。