资讯动态

Oracle审计日志致SYSTEM表空间爆满?从原理到DBMS_AUDIT_MGMT清理实战

发布时间:2026/10/19 5:21:26 来源:尧图企业网站定制
做 Oracle 运维的人应该都见过这种场面某天告警平台突然报 SYSTEM 表空间使用率超过 95%登录数据库一查发现罪魁祸首往往是sys.aud$这张审计表。审计表平时不起眼但一旦数据库开启了审计功能业务量又大它就可能以每天几 GB 的速度疯涨。放任不管的话SYSTEM 表空间被撑爆之后数据库会进入一种半瘫痪状态连普通事务都跑不动应用侧报错一大片。我做过好几轮 Oracle 审计日志清理的整改也在这个过程中踩过不少坑。这篇内容就从清理思路、原理讲解、实际操作到常见坑位完整过一遍尤其会把DBMS_AUDIT_MGMT官方清理体系和手工清理的适用场景讲清楚希望给正在为审计膨胀发愁的 DBA 一份能直接照着做的操作参考。无论你是刚接触 Oracle 的维护人员还是已经带过几个生产库的工程师这篇文章都值得花十分钟读完。1. 审计日志为什么能把一个健康库拖垮1.1 审计功能本身不筛选、不主动归档审计本来是数据库安全管理中的必备能力用来记录“谁在什么时间对什么对象做了什么操作”方便事后追踪和满足合规要求。Oracle 的审计记录可以写到操作系统文件、XML 文件也可以写到数据库内部表。最常见的配置是写入数据库内部也就是AUDIT_TRAILDB或DB_EXTENDED此时所有审计记录会落到SYS.AUD$表中。问题在于数据库审计不会因为记录太多就自动清理旧数据也不会像业务表那样按日期滚动归档。只要审计策略开着它就源源不断往AUD$里写数据。常见的审计策略包括登录审计、权限审计、对象访问审计-- 启用对某个用户的登录审计 AUDIT CREATE SESSION BY app_user; -- 启用对所有对象的 DDL 审计 AUDIT ALL BY app_user BY ACCESS; -- 启用对某张表的增删改查审计 AUDIT INSERT, UPDATE, DELETE ON hr.employees BY ACCESS;一旦这些策略在核心库上开着遇到业务高峰期审计日志增速会非常可观。我之前遇到过一个客户环境高峰期一小时能产生上万条登录审计记录加上应用自身执行的 SQL 被BY ACCESS模式记录下来一个月就能累积出几十 GB 的审计数据。1.2 AUD$ 表的特殊性默认跟 SYSTEM 表空间捆绑更麻烦的是传统审计模式下SYS.AUD$表默认创建在 SYSTEM 表空间里。SYSTEM 表空间是数据库运行的基础内部不仅有数据字典信息各种系统对象也依赖它。当AUD$表持续膨胀SYSTEM 表空间剩余空间会迅速下降。很多人觉得 SYSTEM 表空间不过是一个“放系统数据的表空间”空间不够也没关系。实际上一旦 SYSTEM 表空间无法扩展数据库会直接抛ORA-01653: unable to extend table SYS.AUD$ by N in tablespace SYSTEM而且不只是审计功能出问题普通表的空间分配、字典操作、事务提交等关键行为都可能被影响最终整个库出现大面积阻塞。审计日志清理这件事本质上就是不让AUD$表无限制地吃掉 SYSTEM 表空间。清理前如果只想着“删数据”很可能删完发现空间根本没释放或者删除过程中把 undo 表空间又顶爆了。所以在动手之前先别急着执行DELETE要把当前审计模式、数据量、增长速度先摸清楚。2. 动手清理前先做好四项摸底排查2.1 确认当前审计模式写在哪个参数里审计日志存在哪里最直接的依据是参数AUDIT_TRAIL。用下面的 SQL 看一眼当前值SELECT name, value, isdefault, description FROM v$parameter WHERE name IN (audit_trail, audit_file_dest, audit_syslog_level);常见的取值和含义如下参数值审计记录落点清理目标NONE不启用数据库审计无需处理DB写入SYS.AUD$表清理AUD$表DB_EXTENDED写入SYS.AUD$表额外记录 SQL 文本和绑定变量清理AUD$表但数据量通常更大OS写入操作系统文件清理audit_file_dest目录下的文件XML写入 XML 文件清理 XML 文件XML_EXTENDED写入 XML 文件额外记录 SQL 文本清理 XML 文件很多生产库用的是DB_EXTENDED因为需要记录 SQL 原文做安全审计。这种模式下AUD$表的数据量会更大因为每条 SQL 还附带完整文本空间消耗非常吓人。如果确认AUDIT_TRAIL是DB或DB_EXTENDED还要再判断实际是在传统审计还是统一审计模式下。12c 及以后版本如果启用了统一审计审计记录可能落在统一审计视图中而不在传统的SYS.AUD$表。判断方式很简单-- 查看统一审计是否开启 SELECT value FROM v$option WHERE parameter Unified Auditing;值为TRUE时通常启用的是统一审计清理方式会有差异。这一点后面单独讲。2.2 量化审计表占用量和增长趋势确认审计模式后接着查SYS.AUD$表实际占了多少空间不能凭感觉猜-- 查看 AUD$ 表、索引及其分段占用 SELECT owner, segment_name, segment_type, tablespace_name, ROUND(bytes / 1024 / 1024 / 1024, 2) AS size_gb FROM dba_segments WHERE owner SYS AND (segment_name LIKE AUD$% OR segment_name LIKE AUD\_%) ORDER BY size_gb DESC;也要看一眼 SYSTEM 表空间剩余空间和总体使用率SELECT df.tablespace_name, ROUND(df.total_gb, 2) AS total_gb, ROUND((df.total_gb - NVL(fs.free_gb, 0)), 2) AS used_gb, ROUND(NVL(fs.free_gb, 0), 2) AS free_gb, ROUND((df.total_gb - NVL(fs.free_gb, 0)) / df.total_gb * 100, 2) AS used_pct FROM (SELECT tablespace_name, SUM(bytes) / 1024 / 1024 / 1024 AS total_gb FROM dba_data_files GROUP BY tablespace_name) df, (SELECT tablespace_name, SUM(bytes) / 1024 / 1024 / 1024 AS free_gb FROM dba_free_space GROUP BY tablespace_name) fs WHERE df.tablespace_name fs.tablespace_name AND df.tablespace_name SYSTEM;只看某一时刻的数据还不够最好间隔半小时或一天分别查一次算出审计表增长率。比如今天查到AUD$是 50 GB三天后是 80 GB那就意味着每天的增量超过 10 GB必须尽快做清理方案否则再大的系统表空间也不够撑。2.3 确认清理权限和业务留档要求清理审计日志之前还要问自己一个问题删掉的这些记录业务上是否需要留存很多单位的内部审计规范会要求数据库审计日志至少保留 180 天甚至一年。直接删光所有历史审计数据虽然技术上解决了空间问题但后续外部审计或安全排查时没有历史记录可查会是很严重的合规事故。我通常的做法是先跟相关团队确认“必须保留多长时间”然后保留最近三个月或最近半年的数据只清理更早的归档日期数据。这样既释放了空间又不至于把审计证据全弄丢。3. 手工清理审计日志能用但别蛮干3.1 分批 DELETE 的正确清法如果只是临时救急并且想要保留最近一段时间的数据手工DELETE是最直觉的做法。但直接执行一个大事务删除几十 GB 数据会产生大量 undo 和 redo很容易把 undo 表空间撑爆也可能因为长时间持有锁导致数据库异常。正确做法是分批提交每批删除一小部分数据后立即COMMIT。下面这段 PL/SQL 就是按时间分批删除指定日期前数据的常规写法DECLARE l_delete_before TIMESTAMP : TO_TIMESTAMP(2024-01-01 00:00:00, YYYY-MM-DD HH24:MI:SS); l_batch_size PLS_INTEGER : 5000; l_deleted_count PLS_INTEGER : 0; BEGIN LOOP DELETE FROM sys.aud$ WHERE ntimestamp# l_delete_before AND ROWNUM l_batch_size; IF SQL%ROWCOUNT 0 THEN COMMIT; EXIT; END IF; COMMIT; l_deleted_count : l_deleted_count SQL%ROWCOUNT; DBMS_OUTPUT.PUT_LINE(deleted: || l_deleted_count); END LOOP; END; /这里用ROWNUM 批次大小的方法来限制单次删除记录数能有效控制单个事务的大小。ntimestamp#是AUD$表里记录审计时间的时间戳字段带时区比较时直接用时间戳类型更稳妥。实测下来分批删除比一次性DELETE温和得多至少不会在删除一多半时突然把 undo 表空间顶满。但DELETE有一个天生的缺点表的高水位线不会因为删除了数据就自动降低删完 60 GB段空间可能还是占着 60 GB这个问题后面会专门说。3.2 TRUNCATE 清空审计表先停审计再操作如果要清理的对象是整张AUD$表而且不需要保留任何历史数据很多人会直接执行TRUNCATE TABLE sys.aud$;这个操作本身很快但严重的问题是如果审计功能还在运行会话可能正在往AUD$表里写数据此时直接TRUNCATE会报错或者造成索引不一致。更常见的情况是TRUNCATE需要拿到表的排他锁而审计会话不断写入DDL 一直等待命令卡住。正确姿势应该是先把审计功能关闭等待当前写入停止再执行TRUNCATE最后恢复审计配置-- 第一步关闭审计并持久化到 spfile ALTER SYSTEM SET audit_trail NONE SCOPE SPFILE; -- 第二步重启数据库实例RAC 环境需要滚动重启所有实例 SHUTDOWN IMMEDIATE; STARTUP; -- 第三步清空审计表 TRUNCATE TABLE sys.aud$; -- 第四步恢复审计 ALTER SYSTEM SET audit_trail DB_EXTENDED SCOPE SPFILE; -- 第五步再次重启实例 SHUTDOWN IMMEDIATE; STARTUP;为什么必须重启两次因为audit_trail参数是静态参数SCOPE SPFILE之后要重启才能生效。如果嫌重启代价太高也可以使用SCOPE BOTH在部分版本中动态调整但实测中仍然可能出现审计会话往旧表写数据的情况稳妥起见还是规划一个维护窗口重启。经历过一次TRUNCATE卡死之后我的习惯是先执行SELECT COUNT(*) FROM sys.aud$确认没有会话持续写入同时查一下是否存在活动事务访问AUD$SELECT s.sid, s.serial#, s.status, t.used_ublk FROM v$transaction t, v$session s, v$locked_object lo, dba_objects o WHERE t.addr s.taddr AND lo.session_id s.sid AND o.object_id lo.object_id AND o.object_name AUD$;有结果就说明还有事务和锁不能直接TRUNCATE。3.3 清理之后空间没释放多半是高水位线问题DELETE只是按行删除段的高水位线依然停留在曾经达到的最高位置所以表的存储空间不会自动收缩。清完几张 GB 数据查dba_segments发现占用一点没变这是很正常的现象。如果必须要原地收缩可以对表做MOVE或SHRINK SPACE操作。示例-- 将审计表移动到 SYSAUX 表空间并释放原空间需要先关闭审计并重建索引 ALTER TABLE sys.aud$ MOVE TABLESPACE SYSAUX; -- 重建 AUD$ 表上的索引 ALTER INDEX sys.aud_un_1 REBUILD;这里要注意索引名并不是所有环境都相同先查出来再重建。SHRINK SPACE虽然有在线收缩的优势但对表结构、表空间类型有要求不是所有场景都适合初期建议优先使用MOVE方案。4. 官方推荐用 DBMS_AUDIT_MGMT 定期清理审计日志4.1 先初始化审计清理配置手工DELETE和TRUNCATE都是救急手段长期维护时最好使用 Oracle 官方提供的DBMS_AUDIT_MGMT包。这个包在 11g 中就已经提供专门用于审计日志清理能帮我们管理AUD$、FGA$、OS 审计文件、XML 审计文件的清理策略。第一次使用前要执行INIT_CLEANUP初始化清理配置BEGIN DBMS_AUDIT_MGMT.INIT_CLEANUP( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, default_cleanup_interval 72 -- 默认清理周期单位小时 ); END; /audit_trail_type指定要清理的审计轨迹类型。这里传的是AUDIT_TRAIL_AUD$表示清理传统审计记录。还有AUDIT_TRAIL_FGA$对应细粒度审计AUDIT_TRAIL_OS对应操作系统审计文件AUDIT_TRAIL_XML对应 XML 审计文件AUDIT_TRAIL_UNIFIED对应统一审计记录。这个初始化操作通常只需要执行一次。如果重复执行Oracle 会给出对象已存在之类的提示不影响使用。4.2 设置归档时间戳按需清理历史数据DBMS_AUDIT_MGMT的优势之一是可以设定一个“最后归档时间”只清理这个时间点之前的数据保留最近的数据。用法是先设置归档时间戳再执行清理-- 设置归档时间戳2024-01-01 之前的审计记录都视为已归档可以清理 BEGIN DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, last_archive_time TO_TIMESTAMP(2024-01-01 00:00:00, YYYY-MM-DD HH24:MI:SS) ); END; / -- 执行按归档时间戳清理 BEGIN DBMS_AUDIT_MGMT.CLEAN_AUDIT_TRAIL( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, use_last_arch_timestamp TRUE ); END; /这段逻辑相当于告诉我“2024 年之前的数据允许删”然后包内部自动去分批删除。包在设计上会控制批量删除的节奏比手动写一个巨大的DELETE更容易控制资源消耗。如果想一次性清空所有数据不用归档时间戳可以直接传FALSEBEGIN DBMS_AUDIT_MGMT.CLEAN_AUDIT_TRAIL( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, use_last_arch_timestamp FALSE ); END; /4.3 配置自动清理任务解放双手如果经常面临审计日志膨胀可以配置后台自动清理。数据库允许使用DBMS_AUDIT_MGMT.CREATE_PURGE_JOB创建一个定时任务BEGIN DBMS_AUDIT_MGMT.CREATE_PURGE_JOB( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, audit_trail_purge_interval 24, -- 每 24 小时执行一次 audit_trail_purge_name PURGE_AUD_LOG, use_last_arch_timestamp TRUE ); END; /这里use_last_arch_timestamp TRUE表示每次只清理归档时间戳之前的数据。配合前面定期执行SET_LAST_ARCHIVE_TIMESTAMP就能实现“保留三个月清理三个月前数据”的滚动策略。任务创建后可以通过视图确认SELECT job_name, job_status, last_cleanup_time FROM dba_audit_mgmt_cleanup_jobs;如果哪天不想自动清理了可以禁用或移除任务BEGIN DBMS_AUDIT_MGMT.DROP_PURGE_JOB( audit_trail_purge_name PURGE_AUD_LOG ); END; /这套官方流程的最大好处是清理动作由 Oracle 内部调度避免人工操作带来的误删风险。生产环境中我建议把这个任务的执行时段错开业务高峰期尽量安排到凌晨。4.4 新版数据库统一审计的清理差异如果是 12c 以后启用了统一审计的数据库清理对象就不再是传统SYS.AUD$表而是统一审计记录。对应的audit_trail_type要换成DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED-- 高通设置归档时间戳并清理统一审计记录 BEGIN DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED, last_archive_time TO_TIMESTAMP(2024-01-01 00:00:00, YYYY-MM-DD HH24:MI:SS) ); END; / BEGIN DBMS_AUDIT_MGMT.CLEAN_AUDIT_TRAIL( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED, use_last_arch_timestamp TRUE ); END; /统一审计模式下审计数据主要记录在UNIFIED_AUDIT_TRAIL视图后面的内部表里通常占用的是 SYSAUX 表空间。清理前先用SELECT COUNT(*) FROM unifed_audit_trail看一眼数据量级再决定保留窗口。有一点特别提醒12c 的数据库如果同时启用过传统审计和统一审计清理流程最好不要混用。先确认当前实际生效的模式再选择对应的清理方式。5. 从根本上减轻审计表清理压力5.1 把审计表挪出 SYSTEM 表空间如果版本比较老、系统表空间已经非常紧张一个有效手段是把SYS.AUD$表迁移到 SYSAUX 或者专门的表空间。官方提供的包函数是SET_AUDIT_TRAIL_LOCATIONBEGIN DBMS_AUDIT_MGMT.SET_AUDIT_TRAIL_LOCATION( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, audit_trail_location SYSAUX ); END; /这个操作最好在审计功能暂停的状态下做比如放在维护窗口先把审计关闭执行函数再开启审计。函数执行后Oracle 会把AUD$表及关联对象迁移到新表空间接下来清理和空间压力就不再直接威胁 SYSTEM 表空间了。如果不常用官方包也可以手动ALTER TABLE sys.aud$ MOVE TABLESPACE SYSAUX;然后重建索引。但SET_AUDIT_TRAIL_LOCATION会把内部关联对象一并处理出错概率更小。5.2 分区改造让清理变成“删分区”另外一个长期治理思路是把审计表做成按时间分区。只要把审计表改造成范围分区表清理历史数据就可以变成ALTER TABLE ... DROP PARTITION执行速度比DELETE快很多空间释放也更彻底。但这里要泼一盆冷水直接把系统对象SYS.AUD$改成兼容的分区表在常规运维里并不轻松。需要确认当前数据库版本、审计模式、AUD$表结构通常要规划一个维护窗口在停止审计的前提下做重建。如果你不是非常熟悉 Oracle 内部表结构建议别在生产库硬试优先考虑官方包清理和定时任务方案。5.3 如果审计需求不复杂OS 审计也能分流还有一种思路是从源头分流把数据库审计模式调整成 OS 审计让审计记录直接写到操作系统文件由操作系统层面的日志轮转机制去管理。这样的好处是数据库内部不再产生大表数据SYSTEM 表空间压力瞬间消失。代价也不小OS 审计文件的读取、汇总、分析都不如数据库内部审计方便合规审计时往往需要额外写脚本去解析文本文件。如果业务对审计记录实时性要求高这个方案不一定合适。我的建议是数据库内部使用DBMS_AUDIT_MGMT做定时清理同时把不必要的高频审计策略关掉真正需要长期留档的数据再做外部归档这是目前生产环境最稳妥的组合。6. 一次线上 SYSTEM 表空间告警的处置实录6.1 告警出现SYSTEM 使用率 98%有一次客户的生产库告警SYSTEM 表空间使用率冲到了 98%数据库整体响应变慢部分应用连接超时。登录服务器后我没有急着清理空间先拉了数据库当前等待事件和表空间使用情况结果发现主要是空间分配等待进一步定位到SYS.AUD$表占了 90 GB。当时数据库存在大量登录失败和权限调整审计记录而且AUDIT_TRAIL设置成了DB_EXTENDEDSQL 文本也一并写入导致数据膨胀速度远超预期。短时间内如果 SYSTEM 空间耗尽整个实例都可能挂掉务必优先处理。6.2 定位问题AUD$ 表是绝对主力执行的定位 SQL 很简单SELECT owner, segment_name, segment_type, tablespace_name, ROUND(bytes / 1024 / 1024 / 1024, 2) AS size_gb FROM dba_segments WHERE owner SYS AND tablespace_name SYSTEM ORDER BY bytes DESC;结果前几行全是AUD$的段和索引。再配合v$parameter查到audit_trail DB_EXTENDED基本可以确认核心问题就是审计日志膨胀。6.3 清理动作与结果确认当时业务要求保留最近 90 天审计数据我先用DBMS_AUDIT_MGMT设置了归档时间戳清理 90 天前的数据同时创建定时清理任务避免下次再被动应对-- 设置归档时间戳保留最近 90 天 BEGIN DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, last_archive_time SYSTIMESTAMP - INTERVAL 90 DAY ); END; / -- 执行清理 BEGIN DBMS_AUDIT_MGMT.CLEAN_AUDIT_TRAIL( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, use_last_arch_timestamp TRUE ); END; / -- 创建每日自动清理任务 BEGIN DBMS_AUDIT_MGMT.CREATE_PURGE_JOB( audit_trail_type DBMS_AUDIT_MGMT.AUDIT_TRAIL_AUD$, audit_trail_purge_interval 24, audit_trail_purge_name PURGE_AUD_90D, use_last_arch_timestamp TRUE ); END; /清理过程中我实时监控了v$session里长时间运行的会话和归档日志生成量确认没有异常后才离开现场。执行完毕后 SYSTEM 表空间使用率从 98% 降回到 70% 左右数据库响应恢复正常。那次之后我又把客户环境的无用审计策略做了优化并把不必要的对象审计关掉。后续几个月没有再出现过 SYSTEM 表空间告警。7. 常见问题与避坑速查表7.1 高频故障对照表下面这张表是审计日志清理过程中最容易遇到的问题基本都亲身遇到过现象常见原因处理方式DELETE执行到一半报ORA-30036undo 表空间不足分批提交缩小单批记录数或扩容 undo 表空间TRUNCATE TABLE sys.aud$卡住审计会话仍在写入或存在锁先停审计再查锁最后执行清空清理完空间占用没变化高水位线未降低MOVE或SHRINK SPACE然后重建索引删除时提示权限不足当前用户不能操作 SYS 对象使用SYSDBA或具有足够权限维护窗口执行RAC 环境下单节点清理后重启恢复参数只在部分实例生效所有节点统一修改参数并滚动重启12c 统一审计下清理AUD$无效实际写入统一审计内部表改用AUDIT_TRAIL_UNIFIED清理自动清理任务没有执行任务调度异常或归档时间戳未更新检查dba_audit_mgmt_cleanup_jobs重新设置归档时间戳7.2 几条不会写在文档里的实战经验第一清理审计日志之前最好先把当前数据量投影到一块备份空间尤其在合规要求严的行业。不要清理完才发现某个季度需要回溯历史操作那时再找数据就非常被动。第二不要在业务高峰期直接跑大批量删除。即使分批删除也会产生额外的 redo 和 undo拉高归档日志生成速度。凌晨低峰期执行是最好的选择如果确实紧急也要把批大小调小并盯紧等待事件。第三清理之后顺手重建一下审计表的索引和分析统计信息。AUD$表长期大量增删后索引效率会下降定期DBMS_STATS.GATHER_TABLE_STATS(SYS,AUD$)对后续查询和审计操作都有帮助。第四如果所在环境的数据库版本比较老执行DBMS_AUDIT_MGMT报错先检查是否有未完成初始化或者尝试重新INIT_CLEANUP一次。有些版本在升级后需要重新加载包状态重启实例也能解决。我个人在实际运维中养成的习惯是先确认审计模式再量化增长最后才决定用DBMS_AUDIT_MGMT还是手工方案。清理只是手段真正让数据库稳定运行还要靠定期自动清理机制和合理的审计策略。这块做顺了审计日志就不再是悬在系统表空间头上的一把刀。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑