Oracle 19c SYSAUX表空间暴涨?WRI$_ADV_OBJECTS清理实战指南
2026/9/13 16:57:02 网站建设 项目流程

1. 先说现象:SYSAUX被WRI$_ADV_OBJECTS塞满是什么样的

在ORACLE 19C环境里,如果你发现SYSAUX表空间一直在涨,查了v$sysaux_occupants发现OCCUPANT_NAME是ADVISOR相关的条目占用几十GB甚至上百GB,十有八九就是WRI$_ADV_OBJECTS在作祟。

这个表我之前在一些生产库上遇到过几次,最夸张的一回是单表加LOB段直接吃了SYSAUX将近60GB,整个表空间使用率飙到90%以上,差点把数据库撑爆。当时还在线跑着核心业务,不敢随便重启,硬着头皮花了一个下午把问题啃了下来。后来陆陆续续又碰到过几回,基本流程已经固化下来了,今天把完整的排查和处置思路整理出来,希望能给遇到同样问题的同行省点时间。

WRI$_ADV_OBJECTS这个名字里,WRI代表的是Server Manageability内部对象,ADV是Advisor(顾问框架)的缩写,OBJECTS顾名思义就是各类顾问功能产生的数据对象表。它本质上属于Oracle自动维护任务和Advisor框架的底层存储表,承载着优化器统计信息历史、ADDM快照元数据、SQL调优建议、基线基线、自动维护任务执行记录等多类数据。

问题的麻烦之处在于,这张表在正常情况下很少被DBA直接触碰,但它的膨胀一点不含糊。如果不搞清楚里面到底存了什么、哪些能删、哪些不能删,很容易把自己逼进死胡同——删错了影响顾问功能甚至影响优化器行为,不删又看着SYSAUX空间一路飘红。

2. 为什么会膨胀:搞清楚WRI$_ADV_OBJECTS里装的是什么

2.1 自动维护任务在背后做了什么

19C里默认开启了三个自动维护任务:自动优化器统计信息收集(auto optimizer stats collection)、自动段顾问(auto space advisor)和自动SQL调优顾问(auto SQL tuning advisor)。这三个任务每天在维护窗口(MAINTENANCE WINDOW)内自动运行,而它们的执行元数据、建议结果、历史快照、统计信息版本记录等大量中间产物,都会落到WRI$_ADV_OBJECTS以及它的一系列物化视图上。

最关键的一点是,自动统计信息收集每次运行都会在WRI$_ADV_OBJECTS里登记大量的统计信息历史记录。如果表结构频繁变动、数据量波动大、或者维护窗口内有很多大表在做统计信息更新,这张表的行数会在很短时间内翻好几倍。

我在一个数仓环境的19C库上看过,一个月内WRI$_ADV_OBJECTS的行数从几百万涨到接近三千万,SYSAUX总占用从12GB涨到47GB,期间业务侧根本没做过什么特殊操作,纯粹是每天凌晨的自动收集任务一轮一轮地往里堆数据。

2.2 WRI$_ADV_OBJECTS相关的物化视图和索引

这张表本身是Advisor框架的基础表,在它之上还挂了一系列物化视图,常见的有:

  • WRI$_ADV_OBJECTS本身:主表,存储各类顾问对象元数据
  • WRI$_ADV_SQLT_PLANS:SQL调优顾问的执行计划记录
  • WRI$_ADV_SQLT_TASKS:SQL调优任务信息
  • WRI$_ADV_TASKS:Advisor任务总表
  • WRI$_ADV_JOURNAL:日志类记录
  • WRI$_ADV_RATIONALE:建议的理由说明
  • WRI$_ADV_DIRECTIVES:指令类数据

这些物化视图在创建时有对应的MV名,比如WRI$_ADV_OBJECTS_MV、WRI$_ADV_SQLT_PLANS_MV等。日常使用中,DBA执行DBMS_ADVISOR、DBMS_SQLTUNE、DBMS_STATS等包进行调优或统计信息管理时,都会向这些表写入数据。

索引方面也要留意,WRI$_ADV_OBJECTS上有多个索引,最大的索引如果长期不重建,也会占用相当可观的SYSAUX空间。我当时排查的时候就见过索引段占掉10GB以上的情况。

2.3 为什么19C比老版本更容易膨胀

从11g到12c再到19C,自动维护任务和Advisor框架的功能越来越强,数据量也随之变大。19C中统计信息的历史保留策略尽管默认是31天,但对于表数量特别多(上万张表)的库,每天自动收集产生的新记录可能就是几百万条级别,累积31天就是上亿条。如果你还在用DBMS_STATS包频繁做手动收集,或者开启了实时统计信息(real-time statistics),那写入量会更夸张。

另一个容易被忽略的点:如果你在PDB架构下使用19C,每个PDB都有自己的SYSAUX,WRI$_ADV_OBJECTS的膨胀速度是叠加的。CDB根容器里也有自己的Advisor数据,维护的时候不要只盯着一个容器看。

3. 动手前的诊断:别一上来就DELETE,先查清家底

3.1 快速确认SYSAUX到底被谁占了

进入数据库后,第一步先确认SYSAUX表空间整体使用情况,然后定位到具体对象。我常用的SQL是下面这几条,按顺序跑一遍基本就能锁定目标:

-- 查看SYSAUX表空间使用率 SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024/1024, 2) AS size_gb, ROUND(SUM(CASE WHEN status = 'ONLINE' THEN bytes END)/1024/1024/1024, 2) AS online_gb FROM dba_data_files WHERE tablespace_name = 'SYSAUX' GROUP BY tablespace_name;

如果有范围分区或开了自动扩展,就配合dba_segments排查:

-- 按段大小倒序查看SYSAUX下的TOP对象 SELECT * FROM ( SELECT owner, segment_name, segment_type, ROUND(SUM(bytes)/1024/1024, 2) AS size_mb FROM dba_segments WHERE tablespace_name = 'SYSAUX' GROUP BY owner, segment_name, segment_type ORDER BY size_mb DESC ) WHERE ROWNUM <= 20;

正常情况下你会在TOP列表里看到WRI$_ADV_OBJECTS或者它对应的物化视图排在前面。如果这条SQL跑得很慢,说明SYSAUX里的段数量已经很多了,排除索引、LOB段后确认主表大小,接着可以做下一步深挖。

3.2 查看WRI$_ADV_OBJECTS的行数与时间跨度

锁定目标后,查一下这张表的历史数据分布,判断是单纯的行数暴涨还是高水位线问题。核心是确认数据的写入时间段,以便判断是哪一类维护任务产生的:

-- 查看表行数 SELECT COUNT(*) FROM sys.WRI\$_ADV_OBJECTS; -- 查看数据最早和最晚的写入时间 SELECT MIN(smp_timestamp), MAX(smp_timestamp) FROM sys.WRI\$_ADV_OBJECTS;

SMP_TIMESTAMP是这张表里记录任务执行快照时间的关键列。一般来说,如果MAX时间就在最近一两天,说明自动维护任务还在持续写入,清理完以后如果不调整维护策略,表还会继续涨。如果MIN和MAX的时间跨度并不长(比如只有几天),但行数巨大,说明可能是某个大任务一次性产生了海量记录,比如一次超大范围的SQL调优或统计信息收集。

3.3 判断危险级别:哪些数据能清、哪些尽量别动

在删除前,需要对WRI$_ADV_OBJECTS中的数据成分有个基本判断。这张表里面数据可以粗略分成几类:

可以清理的:历史统计信息快照、旧Advisor任务执行记录、过期基线数据、旧的自动维护任务建议结果。这些数据即便丢失,也不影响数据库正常运行,最多是历史趋势分析时少看几天。

谨慎清理的:正处于执行中或保留期内的SQL调优任务、SQL计划基线、SPM相关的计划历史。如果库上有依赖这些数据做性能分析的场景,删除前最好先和业务确认。

不要乱动的:当前正在使用的统计信息基础数据、活跃的Advisor任务元数据、物化视图刷新所需的元数据。这些一旦误删,轻则相关功能报错,重则影响优化器行为,导致执行计划大面积变化。

实际清理时,我一般不会直接对WRI$_ADV_OBJECTS主表做DELETE,而是通过官方提供的包和过程去清理,只有到了万不得已才考虑直接操作。因为这张表的表结构复杂,外键关联和物化视图依赖多,直接DELETE主表容易引发其他问题。

4. 清理实操:从保命到根治的几种做法

4.1 第一优先:通过DBMS_STATS清理统计信息历史

如果是统计信息历史累积导致的膨胀,最安全、最直接的方法是调用官方包清理。19C下可以用下面的命令调整统计信息保留天数:

-- 查询当前统计信息历史保留天数 SELECT DBMS_STATS.GET_STATS_HISTORY_RETENTION FROM DUAL; -- 将保留天数改为7天 EXEC DBMS_STATS.ALTER_STATS_HISTORY_RETENTION(7);

执行之后,旧于保留期的统计信息历史会自动被后续的维护任务清理掉。但这个方法的问题是它不会立刻释放空间,需要等待清理任务去执行。如果空间已经告急了,可以手动触发清理:

-- 手动清理7天前的统计信息历史 EXEC DBMS_STATS.PURGE_STATS(sysdate - 7);

PURGE_STATS执行完以后,千万记得看WRI$_ADV_OBJECTS的段大小是否真的降下来了。如果行数降了但段大小没变,说明是高水位线问题,需要做段收缩或MOVE。

4.2 第二优先:通过DBMS_AUTO_TASK_ADMIN管控自动维护任务

如果清理完历史数据,这张表还在持续快速增长,就得考虑对自动维护任务进行约束。在不影响核心功能的前提下,可以对自动优化器统计信息收集做降频或者调整窗口,但不建议直接全部禁用。

一个比较稳妥的做法是把统计信息收集的窗口缩短,或者把维护窗口内允许运行的时间压小,这样每次收集产生的数据量会明显减少。我处理过一个案例,客户总说SYSAUX每隔几天涨几个GB,后来发现他们的维护窗口是凌晨0点到6点,库里有9000多张表,其中有大量临时表每天被TRUNCATE重建,导致统计信息的版本记录爆炸式增长。后来把维护窗口从6小时缩到2小时,对临时表设置了NO GATHER的统计信息策略,膨胀速度立刻降下来了。

如果你确实需要临时关闭某个自动维护任务应急:

-- 禁用自动SQL调优顾问 EXEC DBMS_AUTO_TASK_ADMIN.DISABLE( client_name => 'auto sql tuning advisor', operation => NULL, window_name => NULL ); -- 恢复启用 EXEC DBMS_AUTO_TASK_ADMIN.ENABLE( client_name => 'auto sql tuning advisor', operation => NULL, window_name => NULL );

禁用前要确认业务侧是否依赖自动SQL调优给出的建议。大多数OLTP系统即使关掉一段时间问题也不大,但ADDM(自动数据库诊断监控)相关的数据仍然会继续产生,WRI$_ADV_OBJECTS不会因为这个而完全停止增长。

4.3 第三优先:清理Advisor任务和基线数据

如果统计信息历史清理完了,段大小没有明显变化,那基本可以确定是Advisor任务本身产生的数据,这时候要对Advisor任务做定向清理。

查看系统中已有的Advisor任务:

SELECT task_name, task_type, status, created, last_modified FROM dba_advisor_tasks ORDER BY created DESC;

如果有一堆老的、状态为COMPLETED的调优任务长期占用空间,可以调用DBMS_ADVISOR删除。这里要注意,直接DELETE dba_advisor_tasks的底层记录是不推荐的,正确做法是使用DBMS_ADVISOR包中的DELETE_TASK过程:

-- 删除指定任务 EXEC DBMS_ADVISOR.DELETE_TASK('TASK_NAME');

如果没有指定任务名,可以通过SQL批量拼出来,再逐条执行:

SELECT 'EXEC DBMS_ADVISOR.DELETE_TASK(''' || task_name || ''');' FROM dba_advisor_tasks WHERE status = 'COMPLETED' AND created < SYSDATE - 30;

然后把生成的结果逐条执行。这个方法能清掉大量的建议明细、日志、执行计划比对结果等,释放的空间非常可观。

基线(Baseline)数据也要检查。Oracle的基线功能会在WRI$_ADV_OBJECTS中保留大量快照数据,如果发现DBA_HIST_BASELINE里有大量废弃的基线,用DBMS_WORKLOAD_REPOSITORY.DROP_BASELINE清理:

-- 删除指定的固定基线 EXEC DBMS_WORKLOAD_REPOSITORY.DROP_BASELINE( baseline_name => 'BASELINE_NAME', cascade => TRUE );

CASCADE参数加上去会连带删除与基线关联的快照数据,释放空间的效果更直接。

4.4 第四优先:MOVE或SHRINK释放高水位线

当你执行完DELETE或者PURGE之后,行数是降了,但段空间可能依然高居不下。这个现象我遇到太多次了,有DBA清理完数据说"没效果",结果一查段大小还是原封不动,原因就是高水位线没有回落。

此时有两种处理方式:

方式一是对表做MOVE,把数据迁移到新的段中:

-- 先将WRI\$_ADV_OBJECTS迁移到SYSAUX表空间中的新段 ALTER TABLE sys.WRI\$_ADV_OBJECTS MOVE; -- 迁移后必须重建索引 ALTER INDEX sys.WRI\$_ADV_OBJECTS_IDX_01 REBUILD ONLINE; ALTER INDEX sys.WRI\$_ADV_OBJECTS_IDX_02 REBUILD ONLINE;

MOVE操作会让表上的索引失效,所以做完后必须立即批量重建索引。如果你的环境允许短暂锁表,MOVE是最干净利落的手段,直接把段收缩到最合理的大小。如果表上没有锁竞争的顾虑,可以试试ONLINE模式:

ALTER TABLE sys.WRI\$_ADV_OBJECTS MOVE ONLINE;

方式二是使用SHRINK SPACE,要求表所在表空间开启行移动:

ALTER TABLE sys.WRI\$_ADV_OBJECTS ENABLE ROW MOVEMENT; ALTER TABLE sys.WRI\$_ADV_OBJECTS SHRINK SPACE CASCADE;

SHRINK会连带收缩索引和有依赖关系的物化视图,比单纯MOVE一步到位。但SHRINK执行期间对表占用资源更高,在业务高峰期跑容易引发UNDO膨胀和日志量暴增,SHRINK前要提前预估执行窗口。

我个人更推荐先MOVE后重建索引,原因很简单:MOVE是数据库中极其成熟的操作,影响面和执行风险都可控,而SHRINK在部分甲骨文版本上对内部对象表的兼容性存在问题,实测中我就遇到过WRI$_ADV_OBJECTS做SHRINK时触发器报错的案例。

4.5 终极手段:TRUNCATE分区,但这招要有前提

WRI$_ADV_OBJECTS这张表并不是分区表,正常情况下不能直接用TRUNCATE PARTITION。但它在数据组织上会分成若干子表,比如WRI$_ADV_OBJECTS_IDX、WRI$_ADV_OBJECTS_LOB等。如果表空间确实告急,而且已经从数据特征上确认整表的历史数据都是可丢弃的,可以考虑用ALTER TABLE TRUNCATE直接清空整表数据。

之所以说是终极手段,是因为一旦执行,整张表的数据全部丢失,没有任何后悔药。我在生产环境只做过一次,是在业务方明确说"Advisor功能全都不用,历史记录也不需要保留"的前提下,而且事先做了全库的逻辑备份,才敢动这个操作。你如果在测试环境或者非核心环境,倒是可以放心大胆地试一遍,这样能最直观地看到这张表的段降到最小基线有多大。

4.6 清理LOB段:别漏掉这一块

WRI$_ADV_OBJECTS表的LOB段(比如CLOB类型的建议详情列)经常是空间占用的大头。我曾经用dba_segments查这张表,表本身只有1GB,但连带一个LOB段加两个LOB索引段,总共吃了4GB多。因为LOB段的DELETE做得很特殊,即使你删了数据,高水位线也很难自动回落。

对于LOB段的收缩,MOVE操作可以带上LOB子句:

-- MOVE表的同时单独迁移CLOB列到指定表空间(此处仍以SYSAUX为例) ALTER TABLE sys.WRI\$_ADV_OBJECTS MOVE LOB(REPORTCLOB) STORE AS (TABLESPACE SYSAUX);

如果表上有多个LOB列,需要分别列出。这里的REPORTCLOB列名要以实际表结构为准,可以用DBA_LOBS查出来:

SELECT table_name, column_name, segment_name FROM dba_lobs WHERE table_name = 'WRI$_ADV_OBJECTS';

如果你不确定LOB列名,就执行这条SQL确认。把LOB段单独MOVE以后,空间释放立竿见影。

5. 常见问题与排查技巧实录

5.1 清理完数据后SYSAUX空间没有变化

这是出现频率最高的问题。根本原因就是上一节提到的高水位线没有回落,数据文件大小没变,表空间使用率自然没变。处理方法就是MOVE或者SHRINK,先把段空间真正释放出来。

注意SYSAUX表空间的数据文件一旦扩展过,通常不会自动收缩。就算段收缩了,数据文件也还是那么大。如果你想让操作系统层面的文件变小,需要额外的RESIZE操作,但SYSAUX表空间日常使用中不建议把数据文件缩得太小,否则以后自动维护任务一跑又会自动扩展,频繁的RESIZE反而会增加IO开销。

5.2 DELETE时遇到ORA-01555或ORA-30036

直接对WRI$_ADV_OBJECTS执行大批量DELETE时,如果UNDO表空间不够大,很容易触发快照过旧错误。这个表动辄几千万行,一条DELETE把所有历史数据一次性删掉,UNDO容易扛不住,回滚段也会被撑爆。

我的做法是循环分批删除,一次限制删除几万行,用时间条件圈定范围:

DECLARE v_count NUMBER := 1; BEGIN WHILE v_count > 0 LOOP DELETE FROM sys.WRI\$_ADV_OBJECTS WHERE smp_timestamp < SYSDATE - 30 AND ROWNUM <= 50000; v_count := SQL%ROWCOUNT; COMMIT; END LOOP; END; /

分批提交对这个表的删除特别重要,否则一旦中途报错,回滚代价极高。

5.3 清理时遇到物化视图刷新失败

WRI$_ADV_OBJECTS上挂着的物化视图在清理过程中可能出现刷新失败,尤其是当你直接DELETE主表数据而没同步处理MV时。处理思路是查一下ALL_MVIEWS中owner为SYS且名字含WRI$_ADV的物化视图的刷新状态:

SELECT mview_name, refresh_mode, last_refresh_type, last_refresh_date, staleness FROM dba_mviews WHERE owner = 'SYS' AND mview_name LIKE 'WRI$_ADV%';

如果发现STALENESS为NEEDS_COMPILE或刷新失败,重新执行一次物化视图刷新:

EXEC DBMS_MVIEW.REFRESH('SYS.WRI$_ADV_OBJECTS_MV', 'C');

这里要把物化视图名字替换成你环境里实际的MV名。通常是WRI$_ADV_OBJECTS_MV,但不同版本可能有差异。批量刷新时可以用类似逻辑把多个MV都刷一遍。

5.4 清理SQL调优任务时出现外键约束冲突

WRI$_ADV_OBJECTS不是孤立存在的,WRI$_ADV_TASKS、WRI$_ADV_JOURNAL、WRI$_ADV_RATIONALE等表之间存在外键关联。如果你手动去删主表数据,很容易因为外键约束而失败或者产生孤儿数据。解决办法是先从子表删起,最后再删主表;更省事的是直接用DBMS_ADVISOR.DELETE_TASK,它会按依赖关系层层删除。

实际工作中我从来没有直接DELETE过WRI$_ADV_TASKS,因为Advisor框架对任务有完整的生命周期管理,用包删除远比手动操作安全。

5.5 预防机制:让WRI$_ADV_OBJECTS不再疯狂增长

清理一次只是治标,想让这张表以后不再失控,需要在运维策略上下功夫。我总结下来比较有效的东西有三个:

第一个是定期清理统计信息历史。把DBMS_STATS的保留天数根据业务情况设置成7到15天,既不耽误趋势分析,又控制了数据量。注意执行完ALTER_STATS_HISTORY_RETENTION后,旧的统计历史并不会立刻消失,还需要配合PURGE_STATS手动清理一次。

第二个是监控自动维护任务。定期查DBA_AUTO_TASK_CLIENT和DBA_AUTO_TASK_STATUS,确认任务执行没有异常。如果发现某个客户端任务每天运行时间过长,就要审视一下是不是库里的对象数量太多了,决定是否缩小维护窗口或者调整资源限制。

第三个是做好SYSAUX空间趋势监控。我给客户维护的库里,会专门建一张SYSAUX空间占用历史表,每周采一次快照,记录V$SYSAUX_OCCUPANTS的关键条目数据。这样一旦WRI$_ADV_OBJECTS开始异常增长,能在几周内就发现趋势,而不是等SYSAUX表空间彻底满了才惊醒。

5.6 一个决策建议:什么时候需要联系原厂支持

如果你的19C环境出现了WRI$_ADV_OBJECTS异常膨胀,但通过官方包清不掉、物化视图反复刷新失败、或者清理完以后几天内又恢复到同样的水位线,这时候别再硬试了。这种情况通常涉及内部表元数据损坏或版本缺陷,需要原厂支持介入打补丁或者做底层修复。

19C的常见补丁集里就有关于SYSAUX内部表膨胀的修复补丁,不同小版本对应的补丁号不同,自行乱打容易引发新的兼容问题。我的排序原则是:先按上面的常规流程处理,处理不了就尽早提SR,不要耽误时间。

6. 最后再分享一个小经验

WRI$_ADV_OBJECTS这类内部表,处理时最大的忌讳是"眼疾手快"。看到SYSAUX爆了,着急删数据,结果误删到当前统计信息或者活跃任务元数据,后续的麻烦比空间不足还大。拿我自己来说,现在无论环境多紧急,都坚持先跑一轮诊断SQL,把行数、时间跨度、占用段的构成、关联的MV状态全部搞清楚,再决定走哪条清理路径。

空间告急的时候,一个完整的处置顺序大致是:先用PURGE_STATS清统计信息历史,再用DBMS_ADVISOR.DELETE_TASK清调优任务,再检查基线并删除过期基线,最后对表和LOB段做MOVE重建。每做完一步就查一次段大小,确认是否真的释放了空间,然后再决定要不要走下一步。这样既不浪费操作,也不会对系统造成过度影响。

说实话,这个表是Oracle自动管理机制的一个缩影——自动维护功能越强大,底层元数据越复杂,我们做DBA的就越需要在"自动"和"可控"之间找到平衡点。看清它的存储逻辑和数据生命周期,问题其实并不难解。

如果你手头正好有这个故障,建议先别急着动数据库,把诊断SQL跑完,对照本文的步骤一步步来。处理完了之后,记得把SYSAUX的空间监控建起来,别再等下一次告警来找你了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询