1. 先把"自动清理"这件事的负责人找出来
很多人处理 AWR 空间问题的第一反应是跑一句DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS,把 retention 改小,然后等着空间自己掉下来。等了两天发现DBA_HIST_SNAPSHOT里的快照数量的确少了几条,但V$SYSAUX_OCCUPANTS里 AWR 那一行的占用基本没动,于是开始怀疑是不是参数没生效、是不是要重启、是不是得手工删。这类困惑我见过太多次,根子往往不在参数上,而在于没搞清楚 AWR 的数据到底是谁在删、按什么节奏删、删的时候受什么约束。
AWR 的清理不是一个"你设了阈值它就照着执行"的定时器,它是 MMON 后台进程在采集快照之后顺带做的一次例行维护。这个动作有它的触发条件、有它的跳过条件、有它的失败路径。你把它当成一个普通调度任务来理解,就会漏掉一半的坑。
1.1 MMON 的清理节奏与触发前提
MMON(Manageability Monitor Process)是 Oracle 可管理性基础设施的核心后台进程,负责快照采集、AWR 计算、ADDM 任务调度、告警评估等一揽子工作。AWR 的自动清理(内部叫 purge)挂在 MMON 的周期任务里,通常在每小时快照采集完成之后触发。
这里有两个容易被忽略的前提。第一,清理是跟着快照采集走的。如果你把INTERVAL设成了 0(停止自动采集),那么自动清理也就基本停摆了,因为触发点没了。我见过有 DBA 为了省空间把 interval 关掉,结果保留策略形同虚设,历史快照一条不动,等着人工干预。第二,MMON 本身要活着且健康。MMON 被 kill、被 hang 住、或者因为某些内部错误反复重启,清理就不会发生。这种情况下参数看着完全正常,快照就是不动。
判断 MMON 是否正常,最直接的是看告警日志里有没有MMON相关的报错,以及V$PROCESS里能不能找到 MMON 的踪影:
SELECT paddr, pid, spid, program FROM v$process WHERE program LIKE '%MMON%';如果这个查询返回空,或者 MMON 的进程号在短时间内反复变化,那就不是 AWR 参数的问题了,得先救 MMON。
1.2 retention、interval 与快照数量的账要自己算一遍
默认配置是RETENTION=11520(分钟,即 8 天)、INTERVAL=60(分钟)。按这个算,稳态下大约保留11520 / 60 = 192个快照。这是一个很关键的基数,因为后面所有的空间估算都建立在它上面。
SELECT dbid, snap_interval, retention, topnsql FROM dba_hist_wr_control;如果某个库把INTERVAL调成了 15 分钟,但RETENTION还是 11520,那待保留的快照数就变成11520 / 15 = 768个,是原来的 4 倍。单快照的体积跟库的活跃度强相关,一个中等负载的库单个快照可能 1~3MB,768 个就是 1~2GB;如果是高并发、SQL 种类极多的核心库,单快照上到 10MB 以上并不罕见,这时候 SYSAUX 里光 AWR 就能吃掉七八个 GB。所以调 interval 之前一定要把 retention 同步算一遍,这是最基本的一笔账。
还有一类更隐蔽的情况:RETENTION被设成了 0。在部分版本里,RETENTION=0表示"永久保留",而不是"立即删除"。有人看到默认 11520 觉得太大,顺手改成 0,以为是不保留,结果反而把所有快照钉死了。正确的"最短保留"是RETENTION=1440(1 天)这样的正数,而不是 0。
1.3 用一个动作验证清理到底跑没跑
与其猜,不如造一个可观测的场景。我的做法是先记录当前快照水位,然后手动触发一次快照,再等一个 MMON 周期,看最老的快照有没有被推掉。
-- 记录当前最老快照 SELECT MIN(snap_id) AS min_snap, MIN(begin_interval_time) AS oldest FROM dba_hist_snapshot; -- 手动打一个快照,作为时间锚点 EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); -- 观察 purge 是否发生(可查告警日志中的 Clearing Snapshot 信息) SELECT snap_id, begin_interval_time FROM dba_hist_snapshot ORDER BY snap_id FETCH FIRST 5 ROWS ONLY;如果过了两三个小时,最老快照的时间戳纹丝不动,而DBA_HIST_WR_CONTROL里的 retention 明明只有 1 天,那基本可以确定清理链路在某处断了。接下来要做的不是继续调参数,而是去定位断点在哪。
提示:验证时不要在业务高峰做,手动快照会额外增加一次 AWR 计算开销。选一个相对平稳的时段,动作前后各留半小时观察窗口。
2. 五种"策略失效"的现场形态,以及怎么一眼分辨
"清理失效"这四个字太笼统了,它底下其实藏着好几种完全不同的故障形态,处理方式也完全不同。把它们区分开,排查效率能提高一大截。
2.1 参数改了但快照数量完全不变
这是最典型的一种。DBA_HIST_WR_CONTROL显示 retention 已经是 1440,但DBA_HIST_SNAPSHOT里躺着一千多条记录,最老的能追溯到两个月前。
第一件事是确认改的是不是同一个容器。多租户环境下,很多人连到 PDB 里执行MODIFY_SNAPSHOT_SETTINGS,看着不报错,实际上改的是 CDB 层的配置——或者说,在部分版本里这个包在 PDB 中执行根本不会影响 CDB 的 AWR 保留策略。正确的做法是连到CDB$ROOT,以SYSDBA身份执行。
第二件事是确认改完之后有没有被别的机制覆盖。有些环境里存在初始化脚本、监控平台下发任务、或者 DBA 交接时留下的定时作业,会在每次实例启动后把参数刷回默认值。查一下DBA_SCHEDULER_JOBS里有没有可疑的自定义作业,以及$ORACLE_HOME下的启动脚本里有没有硬编码的MODIFY_SNAPSHOT_SETTINGS调用。
SELECT owner, job_name, enabled, repeat_interval, job_action FROM dba_scheduler_jobs WHERE job_action LIKE '%WORKLOAD_REPOSITORY%' OR job_name LIKE '%AWR%';2.2 快照在减少,但 SYSAUX 空间一点没降
这种情况最让人抓狂,因为你做的每一步看起来都"生效"了。快照数从 1200 掉到 200,告警还在响。原因很简单:DELETE 只释放行,不释放段。
AWR 的底层表是一批WRH$_前缀的分区表,删除快照对应的行之后,这些段的高水位(HWM)并不会自动下降。SYSAUX 表空间的占用是按段分配的,段没缩,空间就不还。这一点在第 4 节会展开讲,这里先记住结论:快照数下降 ≠ 空间下降,判断空间是否真的回收,要看DBA_SEGMENTS而不是DBA_HIST_SNAPSHOT。
SELECT segment_name, ROUND(SUM(bytes)/1024/1024) AS mb FROM dba_segments WHERE segment_name LIKE 'WRH$%' GROUP BY segment_name ORDER BY 2 DESC FETCH FIRST 15 ROWS ONLY;2.3 快照卡在某个时间点就再也不往下减
快照数量停在一个固定值,最老的快照时间也不动,但系统整体运行正常。这种"卡住"的形态,八成是基线在作祟,或者是某个特性正在引用这批快照。
基线(Baseline)是一组被显式标记为"要长期保留"的快照范围。固定基线不会过期,范围内的快照 MMON 不会删。如果有人在半年前为了排查一次性能问题建了一个基线,事后忘了清理,那么这半年里所有落在基线区间内的快照都会被钉住。更麻烦的是移动基线(Moving Window Baseline),它的窗口大小如果被调到很大,效果类似。
SELECT baseline_name, baseline_type, start_snap_id, end_snap_id, moving_window FROM dba_hist_baseline ORDER BY start_snap_id;除了基线,还有几个东西会引用快照:正在运行的 ADDM 任务、SQL 调优集(SQL Tuning Set)、SQL 性能分析器(SQL Performance Analyzer)的实验、正在导出中的 AWR 数据。这些引用在清理时可能被跳过或延后,排查时要一并看。
2.4 只在某个实例上不清理
RAC 环境下偶尔会遇到:节点 1 的快照按策略正常滚动,节点 2 的快照越堆越多。AWR 的保留策略是库级配置,两个节点共享同一个DBA_HIST_WR_CONTROL,所以理论上不该有差异。出现差异通常意味着节点 2 的 MMON 有问题——可能是该实例的 MMON 一直在报错重启,也可能是该节点的 SYSAUX 剩余空间触发了保护逻辑。
查节点间的差异:
SELECT instance_number, COUNT(*) AS snap_cnt, MIN(begin_interval_time) AS oldest, MAX(begin_interval_time) AS newest FROM dba_hist_snapshot GROUP BY instance_number;如果某个实例的记录长期不增长,说明它连采集都停了,问题比清理失效更严重,得先看那个实例的告警日志。
2.5 清理跑了一半就报错退出
最隐蔽的一种。告警日志里能看到类似ORA-12012的作业执行错误,或者ORA-04021超时,或者更直接的ORA-01653表空间无法扩展。这类错误说明清理动作确实启动了,但在执行过程中被资源问题打断了。
删除 AWR 数据本身是一个写操作,会产生 undo 和 redo。如果一个库的 undo 表空间本来就吃紧,大批量删除很可能把 undo 撑爆,事务回滚,清理失败。SYSAUX 表空间只剩几十兆的时候更是如此——删除虽然释放行,但在某些操作路径下(比如分区维护、索引维护)仍然需要临时空间。
-- 看 undo 使用情况 SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024) AS total_mb FROM dba_data_files WHERE tablespace_name LIKE '%UNDO%' GROUP BY tablespace_name; -- 看 SYSAUX 剩余 SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024) AS total_mb FROM dba_data_files WHERE tablespace_name = 'SYSAUX' GROUP BY tablespace_name;这一类故障的处理顺序是:先给 SYSAUX 和数据文件加空间(或者加数据文件),再手动执行一次小范围的DROP_SNAPSHOT_RANGE验证删除通路是否健康,最后再谈自动清理。
3. 基线:最容易被忽略的空间黑洞
如果说 AWR 清理失效有"头号嫌疑犯",那一定是基线。原因很现实:建基线是一秒钟的事,删基线要有人记得;而记住这件事的人往往已经调岗了。
3.1 固定基线与移动基线的保留语义完全不同
固定基线(Fixed Baseline)用CREATE_BASELINE显式创建,它圈定一段 snap_id 区间,落在这个区间内的快照会被保护,不参与自动清理。除非你显式DROP_BASELINE,否则它一直存在,跨实例重启、跨版本升级都不消失。
移动基线(Moving Window Baseline)是另一套逻辑,它始终跟随着"最近 N 天"这个窗口自动滚动,AWR_BASELINE_STRATEGY相关的自适应阈值计算会用到它。移动基线本身不会额外钉住老快照——它只关心最近的窗口。但如果你把移动窗口的大小(MOVING_WINDOW)调得比 retention 还大,就会出现窗口内的快照"想留但已被清理"的矛盾,部分版本的行为是让 retention 实际上被抬高。所以这两个值应该保持retention >= moving_window的关系。
-- 查看并调整移动窗口 SELECT dbid, baseline_name, moving_window, expiration FROM dba_hist_baseline WHERE baseline_type = 'MOVING_WINDOW'; EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_BASELINE_WINDOW_SIZE(window_size => 5);单位是"天"。默认 8 天,跟默认 retention 对齐。把 retention 从 8 天砍到 2 天,却忘了同步改移动窗口,是配错组合里的常客。
3.2 亲手查一遍哪些快照被基线钉住了
光看基线表还不够,要把基线和快照的实际分布叠在一起看,才知道空间是被哪一段吃掉的。我的习惯是先按天聚合快照,看哪些日期段的快照特别密集,再对照基线区间。
-- 按天看快照分布,找出异常密集的日期段 SELECT TRUNC(begin_interval_time) AS snap_day, COUNT(*) AS snap_cnt FROM dba_hist_snapshot GROUP BY TRUNC(begin_interval_time) ORDER BY 1; -- 把基线区间和快照时段叠起来 SELECT b.baseline_name, b.baseline_type, s.start_snap_id, s.end_snap_id, (SELECT MIN(begin_interval_time) FROM dba_hist_snapshot WHERE snap_id = b.start_snap_id) AS bis_begin, (SELECT MAX(begin_interval_time) FROM dba_hist_snapshot WHERE snap_id = b.end_snap_id) AS bis_end FROM dba_hist_baseline b;如果发现某个基线区间覆盖了上千个快照,而那个区间根本没人用了,那就是几百兆甚至几个 GB 的纯浪费。
3.3 删基线的动作顺序与误删代价
删基线之前必须先想清楚:这个基线是不是某个性能对比分析的基准?如果批处理任务的优化报告还在引用它,删掉后报告就跑不出来了。我的习惯是先给基线重命名打个标记,观察一周没人来找,再动手删。
-- 第一步:改名标记 EXEC DBMS_WORKLOAD_REPOSITORY.RENAME_BASELINE( old_baseline_name => 'BASELINE_20240115', new_baseline_name => 'BASELINE_20240115_TO_DROP'); -- 第二步:确认无引用后删除 EXEC DBMS_WORKLOAD_REPOSITORY.DROP_BASELINE( baseline_name => 'BASELINE_20240115_TO_DROP', dbid => NULL);注意DROP_BASELINE只解除保护,不会自动删除快照。解除保护之后,那些快照要等下一次 MMON 清理周期才会被真正删掉。所以删完基线立刻看空间是不会变的,这个时间差要提前说清楚,否则又是一轮"怎么还是没效果"的自我怀疑。
注意:如果基线区间内存在被 SQL Tuning Set 或其他特性引用的快照,删除基线后这些快照仍可能被跳过。判断方法是删完之后观察
DBA_HIST_SNAPSHOT的最老记录有没有往前推进。
4. SYSAUX 不回收空间的真相:DELETE 只是打了个标记
我见过太多人卡在这一步:参数对了,基线清了,快照确实少了几百条,V$SYSAUX_OCCUPANTS里 AWR 对应的SPACE_USED_MB也降了,但SPACE_ALLOCATED_MB和表空间的真实剩余空间纹丝不动,告警依旧。这个现象不是 bug,是段空间管理的基本原理。
4.1 USED 与 ALLOCATED 的差异从哪来
V$SYSAUX_OCCUPANTS里有两列很关键:SPACE_USED_MB表示当前有效数据占用,SPACE_ALLOCATED_MB表示已经分配给该组件的空间。删数据降的是前者,后者要靠段的物理收缩或重建才会降。
SELECT occupant_name, ROUND(space_used_mb) AS used_mb, ROUND(space_allocated_mb) AS alloc_mb FROM v$sysaux_occupants ORDER BY space_allocated_mb DESC FETCH FIRST 10 ROWS ONLY;如果 used 和 alloc 差了十几个 GB,那就说明空间被"占着不用"。这时候继续删快照是没有意义的,得做段级别的整理。
4.2 分区表场景下可以直接 DROP PARTITION
AWR 的主表(WRH$_SQLSTAT、WRH$_ACTIVE_SESSION_HISTORY、WRH$_SYSTEM_EVENT等)在 11g 之后基本都是按DBID + SNAP_ID做范围分区,并且启用了 INTERVAL 自动分区。这意味着老数据通常集中在少数几个分区里,直接DROP PARTITION比 DELETE 高效得多,而且能立刻释放空间。
-- 先看哪个表的分区最占地方 SELECT table_name, partition_name, ROUND(bytes/1024/1024) AS mb, num_rows FROM dba_tab_partitions WHERE table_name LIKE 'WRH$%' ORDER BY bytes DESC FETCH FIRST 20 ROWS ONLY;找出最老的那批分区,确认里面没有仍然需要的快照,然后逐个 drop。这里有个细节:分区名和 snap_id 的对应关系不是线性的,不能靠猜。要先从DBA_TAB_PARTITIONS的HIGH_VALUE里解析出边界,或者用DBA_HIST_SNAPSHOT确认目标 snap_id 落在哪个分区。
ALTER TABLE WRH$_SQLSTAT DROP PARTITION WRH$_SQLSTAT_12345 UPDATE GLOBAL INDEXES;UPDATE GLOBAL INDEXES这个子句很关键。不带它,drop 之后全局索引会失效,下次 AWR 报表查询可能直接报ORA-01502。带上它会稍微慢一点,但换来的是索引可用性,值得。
4.3 MOVE 和 SHRINK 的实操顺序与空间风险
对于没分区或者分区粒度不合适的情况,只能用MOVE或SHRINK SPACE。这里有个反直觉的地方:MOVE 操作需要额外的空间,因为它实际上是"新建一份、拷过去、再删旧的"。在一个 SYSAUX 只剩 2GB 的紧张环境里贸然 MOVE 一张 8GB 的表,很可能直接把表空间撑爆,变成更大的事故。
安全顺序应该是这样的:
- 先给 SYSAUX 加数据文件,确保有足够余量(至少是目标表大小的 1.2 倍)
- 从最小的表开始 MOVE,验证流程
- 每次 MOVE 后立刻
REBUILD对应的索引 - 观察
DBA_SEGMENTS的变化,确认空间确实释放 - 再处理大表
-- 单表整理示例 ALTER TABLE WRH$_SYSTEM_EVENT MOVE; ALTER INDEX WRH$_SYSTEM_EVENT_PK REBUILD ONLINE; -- 或者用 shrink(需要表空间开启 segment space management auto) ALTER TABLE WRH$_SYSTEM_EVENT SHRINK SPACE CASCADE;SHRINK SPACE CASCADE会连带收缩依赖的索引,操作上省事,但它对并发 DML 的影响比 MOVE 小,代价是需要表所在表空间启用自动段空间管理。如果是老库还停留在手动段管理,就只能走 MOVE 路线。
提示:如果 SYSAUX 已经从 30GB 涨到 60GB,靠 MOVE 硬整理风险很大。更稳妥的做法是新建一个临时表空间,把部分 WRH$ 表
MOVE ... TABLESPACE过去,整理完再迁回,或者干脆规划一次 SYSAUX 的重建窗口。
5. 一次完整的排查链路实录
理论说完了,走一遍实际的排查过程,把前面散落的点串成一条线。以下场景基于一个典型的"改完参数没效果"现场,细节做了脱敏处理。
5.1 从告警日志和现象描述切入
接手时的现象描述是:SYSAUX 使用率 92%,AWR 组件占用 38GB,RETENTION已经改成 3 天,快照数仍然有一千多条。
第一步永远是看告警日志,而不是急着查视图:
# 在告警日志目录下定位关键信息 grep -iE "ORA-12012|ORA-04021|ORA-01653|MMON|Clearing Snapshot" alert_*.log | tail -50结果看到两类信息:一是大量的Clearing Snapshot正常信息,说明清理动作确实在跑;二是夹杂着几条ORA-01653: unable to extend table SYS.WRH$_...报错。这就对上了——清理发生了,但删除过程中的某些操作因为空间不足失败了,事务回滚,快照没删掉。
5.2 视图层层剥到根因
接下来按顺序过视图,每一步都冲着"排除一个可能性"去。
-- 第一层:确认参数 SELECT snap_interval, retention FROM dba_hist_wr_control; -- 结果:60 / 1440,参数正常 -- 第二层:确认快照分布 SELECT COUNT(*), MIN(begin_interval_time), MAX(begin_interval_time) FROM dba_hist_snapshot; -- 结果:1123条,从3个月前到今天 -- 第三层:确认有没有基线钉住 SELECT baseline_name, start_snap_id, end_snap_id FROM dba_hist_baseline; -- 结果:一条固定基线,覆盖了最早期的900个快照 -- 第四层:确认 SYSAUX 状态 SELECT occupant_name, ROUND(space_used_mb) AS used_mb, ROUND(space_allocated_mb) AS alloc_mb FROM v$sysaux_occupants WHERE occupant_name = 'SM/AWR'; -- 结果:used 22GB,alloc 38GB,差值 16GB四层查下来,根因清晰了:基线钉住了 900 个老快照,这些快照删不掉;剩下能删的部分在删除时又因为表空间不足中断;同时 16GB 的已分配空间从没被回收过。三个问题叠在一起,任何一个不解决,现象都不会变。
5.3 修复动作与观察窗口
修复按风险从低到高排:
- 先加空间。给 SYSAUX 加两个数据文件,各 2GB,确保后续操作有腾挪余地。这一步不做,后面全是空谈。
- 解除基线保护。给基线改名打标记,观察两天确认没有报告在引用,然后
DROP_BASELINE。 - 手动删一段验证通路。挑一个较老的范围,用
DROP_SNAPSHOT_RANGE试删。
EXEC DBMS_WORKLOAD_REPOSITORY.DROP_SNAPSHOT_RANGE( low_snap_id => 100, high_snap_id => 300, dbid => NULL);这一步成功了,说明删除通路健康;如果失败,报错信息会直接指出是空间问题还是引用问题。
- 等 MMON 自动清理消化剩余部分。每天观察一次最老快照的时间戳往前推进了多少,正常情况下一天能推掉一天的量。
- 空间整理放到最后一个窗口做。等快照数和 used_mb 都降到目标值,再做分区 drop 或 MOVE,把 alloc 降下来。
整个过程的观察周期我给的是两周。第一周看快照推进,第二周做空间整理。急不得,一次全做反而看不清是哪一步起了作用。
6. 多租户、RAC 和版本差异带来的额外变量
同样的操作在不同架构上表现不同,这是 AWR 维护里最容易被忽略的一层。
6.1 CDB/PDB 下的设置位置陷阱
多租户环境下,AWR 的基础设施统一建在 CDB 层,WRH$系列表也都在CDB$ROOT。在 PDB 里执行MODIFY_SNAPSHOT_SETTINGS,部分版本会成功返回但只影响 PDB 自己的视图,另一些版本会直接报错。无论哪种,CDB 层的实际保留策略都不会被改动。
正确的做法是始终连到CDB$ROOT,用SYS执行。如果你想按 PDB 精细控制,需要用DBMS_WORKLOAD_REPOSITORY里带container参数的重载版本(版本支持情况不一,执行前先查文档)。
-- 连到 CDB$ROOT 执行 ALTER SESSION SET container = CDB$ROOT; EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS( retention => 2880, interval => 60);另外注意,PDB 的快照数据虽然存在 CDB 的WRH$表里,但按CON_DBID区分。查空间占用时要按CON_DBID分组,否则会把某个 PDB 的异常当成整体问题。
SELECT con_dbid, COUNT(*) AS snap_cnt FROM dba_hist_snapshot GROUP BY con_dbid;6.2 RAC 各实例快照的差异
RAC 下每个实例独立采集自己的快照,同一个SNAP_ID在不同实例的BEGIN_INTERVAL_TIME可能相差几分钟。这意味着你按SNAP_ID做批量删除时,会同时删掉多个实例的数据,这是预期行为。
但要注意INSTANCE_NUMBER这个维度在空间分析里的作用。如果某个实例的负载远高于其他实例,它的WRH$_ACTIVE_SESSION_HISTORY会显著更胖。排查空间异常时,按实例分组看分布能帮你定位是哪个节点在"贡献"体积。
SELECT instance_number, COUNT(*) AS rows_cnt FROM wrh$_active_session_history GROUP BY instance_number ORDER BY 2 DESC;另一个细节是 RAC 下的 MMON 是每个实例各有一个,各自负责自己实例的快照清理。所以出现"一个节点清理正常、另一个不正常"是完全可能的,前面 2.4 节讲过。
6.3 几个值得留意的版本行为差异
不同大版本在 AWR 清理上有几处行为不一致,跨版本迁移或者混合环境时要特别小心。
| 场景 | 较早版本常见行为 | 较新版本常见行为 |
|---|---|---|
RETENTION = 0 | 表示永久保留,不清理 | 语义保持一致,但部分小版本有差异,需实测确认 |
INTERVAL = 0 | 停止自动采集,清理也随之停摆 | 同左,但手动快照仍可创建 |
| 基线删除后的快照 | 要等下一个 MMON 周期才释放 | 行为一致,存在数小时延迟 |
| 分区自动管理 | 需手动建分区 | INTERVAL 分区自动创建,drop 更简单 |
DROP_SNAPSHOT_RANGE | 遇基线引用时行为不完全一致 | 通常会有明确报错或跳过逻辑 |
最稳妥的做法是:任何一次跨版本升级后,都重新验证一遍保留策略是否按预期生效,别默认它还会按老样子跑。我在一次小版本升级后就遇到过清理节奏从"每小时检查"变成"每两小时"的情况,虽然最终不影响结果,但观察窗口内很容易误判成失效。
7. 我自己的一套长期维护动作清单
踩过的坑多了,就会形成一套固定的检查习惯。这套动作我现在每个季度跑一遍,基本不会再出现"空间悄悄涨到 90%"的情况。
第一步,每月固定看一次快照数量和保留天数的匹配度。用一句 SQL 就能覆盖:
SELECT (SELECT COUNT(*) FROM dba_hist_snapshot) AS snap_cnt, (SELECT retention/1440 FROM dba_hist_wr_control) AS keep_days, (SELECT COUNT(*) FROM dba_hist_baseline) AS baseline_cnt, (SELECT ROUND(SUM(bytes)/1024/1024/1024) FROM dba_segments WHERE segment_name LIKE 'WRH$%') AS awr_gb FROM dual;snap_cnt和keep_days * 1440 / interval差太多,就去查基线;awr_gb远大于used_mb,就安排一次空间整理。
第二步,建基线的时候强制加命名规范,比如统一用BIS_项目_日期的格式,并且在变更记录里登记。两年后接手的人靠这个名字就能判断该不该删。
第三步,把 SYSAUX 的告警阈值从 85% 提前到 75%。等到 85% 再动手,往往已经没有空间做 MOVE 了,处理成本翻好几倍。
第四步,永远先手动验证再依赖自动。无论是改 retention 还是删基线,操作完都手动打一个快照、观察一个 MMON 周期,确认链路是通的。这个习惯帮我挡掉了至少三次"参数看着对但根本没生效"的隐患。
最后分享一个不太常见但很实用的小技巧:如果 SYSAUX 空间极度紧张、连加数据文件的余量都没有,可以先在另一个表空间里创建一张与WRH$_表结构相同的临时表,把最近需要保留的快照数据INSERT ... SELECT过去,然后直接TRUNCATE原表。这个方法能瞬间释放空间,代价是要中断 AWR 历史数据的连续性——所以只能作为最后手段,且操作前必须完整备份DBA_HIST_SNAPSHOT的元数据。我用过一次,是在一个磁盘已经写满、任何 DDL 都做不了的极端现场,事后花了半天补回数据字典的一致性,不算优雅,但确实救回了实例。