必须用SQL钉死“谁在涨、怎么涨、什么时候涨”:通过DBA_HIST_TBSPC_SPACE_USAGE查趋势与暴增时间点,结合DBA_SEGMENTS按segment_type分类定位对象,再用V$SORT_USAGE和DBA_ROLLBACK_SEGS排查临时段与回滚异常,最后用DBA_FREE_SPACE验证碎片。

表空间增长过快,不能靠猜,必须用 SQL 把“谁在涨、怎么涨、什么时候涨”三件事一次性钉死。盲目加数据文件或 shrink 表,可能掩盖真因,甚至引发锁表或 AWR 断档。
查表空间整体使用趋势和暴增时间点
先看是不是全局性膨胀,还是某几天突然飙升——这对判断是“缓慢渗漏”还是“突发写入”至关重要。DBA_HIST_TBSPC_SPACE_USAGE 是带时间戳的权威视图,比实时 dba_free_space 更可信:
执行以下语句(替换 &tablespace_name 为实际表空间名,如 'SYSAUX'):
SELECT snap_id,
TO_CHAR(s.begin_interval_time, 'yyyy-mm-dd hh24:mi') begin_time,
ROUND(u.tablespace_usedsize * ts.block_size / 1024 / 1024, 2) used_mb,
ROUND((u.tablespace_usedsize - LAG(u.tablespace_usedsize) OVER (ORDER BY snap_id)) * ts.block_size / 1024 / 1024, 2) delta_mb
FROM dba_hist_tbspc_space_usage u
JOIN v$tablespace t ON u.tablespace_id = t.ts#
JOIN dba_hist_snapshot s ON u.snap_id = s.snap_id
JOIN dba_tablespaces ts ON t.name = ts.tablespace_name
WHERE t.name = UPPER('&tablespace_name')
AND s.end_interval_time > SYSDATE - 7
ORDER BY snap_id DESC;重点关注 delta_mb 列:连续多小时单次增长超 500MB,基本可锁定为异常写入窗口;若某天凌晨 2 点突增 3GB,就直接去查那个时段的 DBA_HIST_SEG_STAT 和告警日志。
- 如果没开 AWR 或保留期太短(
dba_hist_wr_control.retention小于 7 天),这条查不到数据,得切到实时段增长分析 - 多租户环境注意加
con_id过滤,否则会混入 PDB 数据 -
v$tablespace中的ts#是内部 ID,别和dba_data_files.file_id混用
定位具体占用对象:别只看 dba_segments,要分类型下钻
dba_segments 能告诉你“谁占得多”,但无法区分是业务表、AWR 表、LOB 段还是导出残留。必须结合 segment_type 和命名规律交叉判断:
运行此语句(替换 YOUR_TABLESPACE_NAME):
SELECT owner, segment_name, segment_type,
ROUND(bytes/1024/1024/1024, 2) gb,
CASE
WHEN segment_name LIKE 'WRH$%' THEN 'AWR snapshot'
WHEN segment_name LIKE 'WRI$%' THEN 'Stats/Advisor'
WHEN segment_name LIKE 'SYS_EXPORT_%' THEN 'expdp residue'
WHEN segment_type = 'LOBSEGMENT' THEN 'LOB storage'
ELSE 'ordinary object'
END category
FROM dba_segments
WHERE tablespace_name = 'YOUR_TABLESPACE_NAME'
ORDER BY bytes DESC
FETCH FIRST 10 ROWS ONLY;常见误判点:
-
WRH$_ACTIVE_SESSION_HISTORY占大头 → 不是业务问题,是 AWR 快照未清理或采样频率过高 -
WRI$_OPTSTAT_HISTGRM_HISTORY排前三 → 统计信息历史保留太久,不是“表太大”,是dbms_stats.get_stats_history_availability返回值过大 -
SYS_EXPORT_SCHEMA_01类对象存在 → expdp 导出中断残留,不是业务数据,不能 truncate,应 drop table -
LOBSEGMENT占比高但 owner 是业务用户 → 查dba_lobs确认归属表,再看该表是否有未清理的附件/日志字段
查临时段和回滚段异常:temp 和 system 表空间爆满的隐藏推手
临时表空间(temp)和 system 表空间暴涨,往往不是对象本身变大,而是排序、建索引、大事务撑爆了临时段或系统回滚段。这类问题不会出现在 dba_segments 里:
查当前活跃大排序:
SELECT s.sid, s.serial#, s.sql_id, s.program,
t.blocks * 8 / 1024 mb_used,
s.sql_text
FROM v$sort_usage t
JOIN v$session s ON t.session_addr = s.saddr
WHERE t.blocks > 100000 -- >800MB
ORDER BY t.blocks DESC;查 system 表空间里的回滚段异常:
SELECT segment_name, status, blocks FROM dba_rollback_segs WHERE tablespace_name = 'SYSTEM' AND status != 'OFFLINE';
关键信号:
-
v$sort_usage.blocks持续 >50000 且对应 SQL 是CREATE INDEX或ORDER BY大结果集 → 加大pga_aggregate_target或改用并行 -
dba_rollback_segs里出现非SYSTEM的 rollback segment 在 system 表空间 → 说明有 DDL 操作未提交,或隐式事务卡住 -
v$sort_usage中sql_text显示INSERT /*+ APPEND */+ 大量子查询 → 可能触发大量 temp segment,检查是否缺少绑定变量导致硬解析风暴
验证碎片与真实可用空间:为什么 dba_free_space 显示有空闲却报 ORA-01653?
表空间显示使用率 95%,但 dba_free_space 里 sum(bytes) 仍有 2GB,却仍无法分配新区段——这是典型碎片问题。Oracle 分配要求连续空闲块,不是总和够就行:
运行这个检查最大连续空闲块:
SELECT tablespace_name, MAX(bytes)/1024/1024 max_chunk_mb FROM dba_free_space GROUP BY tablespace_name ORDER BY max_chunk_mb;
如果返回值远小于你要分配的对象大小(比如建一个 500MB 表失败,但 max_chunk_mb 只有 120),说明必须 shrink 或 move:
-
ALTER TABLE ... SHRINK SPACE COMPACT前必须ENABLE ROW MOVEMENT,否则报错ORA-10636 -
LOBSEGMENT不支持 shrink,只能ALTER TABLE ... MOVE LOB(...) STORE AS (...) - 对
WRH$表做TRUNCATE PARTITION前,先确认分区名:SELECT partition_name FROM dba_tab_partitions WHERE table_name = 'WRH$_ACTIVE_SESSION_HISTORY' ORDER BY high_value DESC FETCH FIRST 1 ROW ONLY;
真正麻烦的从来不是“怎么清”,而是“清完会不会影响监控、审计或统计功能”。比如删掉 AUDSYS.AUD$UNIFIED 会断统一审计链路,flush_awr 不跟 drop_snapshot_range 一起跑,空间根本回收不了。每一步操作前,都得对着 v$sysaux_occupants 或 dba_hist_wr_control 再核一次依赖关系。


















