查dba_segments需先按owner+segment_name聚合再排序,否则分区表、lob、索引等多段结构会导致top 10失真;应sum(bytes)分组,关联dba_tables补行数,并核查lob/index归属及表空间真实剩余空间。

查 DBA_SEGMENTS 时必须先聚合再排序
直接 SELECT * FROM dba_segments WHERE tablespace_name = 'USERS' ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY 看起来快,但结果不可信。因为一张分区表、带 LOB 的表、或有多个索引的表,在 dba_segments 里会拆成十几甚至上百个独立段(segment_type 各不相同),比如 SALES_P2025Q1、SALES_P2025Q2、SYS_LOB000012345C00002$$、SALES_IDX1 —— 它们各自排大小,TOP 10 里全是碎片,真正吃空间的逻辑表反而被淹没。
正确做法是按逻辑归属聚合:同一 owner + 同一 segment_name(哪怕 segment_type 不同)视为一个业务实体的组成部分。例如 DOC_TABLE 的主表段、LOB 段、索引段都应加总。
- 用
SUM(bytes)聚合,不是裸查bytes -
GROUP BY owner, segment_name,忽略segment_type才能合并物理存储 - Oracle 12c+ 必须用
FETCH FIRST 10 ROWS ONLY;11g 及更早请用子查询套ROWNUM,但需确保外层已排序完成
区分 TABLE 和非 TABLE 段类型,避免误判主体
聚合后仍要留意 segment_type 构成。如果 TOP 10 里大量是 INDEX 或 LOBSEGMENT,说明业务表本身不大,但索引设计或大字段使用不合理——比如一个 CLOB 字段存了上万条日志,实际空间全在 LOB 段里,而基表段只占几 MB。
这时不能只盯着 segment_name 是什么表名,得顺藤摸瓜:
- 对
segment_type = 'LOBSEGMENT'的记录,查dba_lobs关联真实所属表:SELECT table_name, column_name FROM dba_lobs WHERE segment_name = 'SYS_LOB000012345C00002$$' - 对
segment_type = 'INDEX',用dba_indexes.table_name回溯基表 - 若
owner是SYSTEM或SYSAUX,别急着处理,先确认是否属 AWR 快照、统计信息历史等系统组件
关联 dba_tables 获取行数和统计信息
光看字节数不够。一张 10GB 的表只有 10 行,大概率是高水位或历史碎片;另一张 8GB 的表有 5000 万行,才是真·业务大户。所以聚合完后,最好左连接 dba_tables 补充 num_rows 和 blocks:
SELECT s.owner,
s.segment_name,
ROUND(SUM(s.bytes)/1024/1024) size_mb,
t.num_rows,
t.blocks
FROM dba_segments s
LEFT JOIN dba_tables t
ON s.owner = t.owner AND s.segment_name = t.table_name
WHERE s.tablespace_name = 'USERS'
AND s.segment_type = 'TABLE'
GROUP BY s.owner, s.segment_name, t.num_rows, t.blocks
ORDER BY SUM(s.bytes) DESC
FETCH FIRST 10 ROWS ONLY;
注意:dba_tables.num_rows 是上次 ANALYZE 或 DBMS_STATS 收集的结果,可能过期;若为 NULL,说明未统计,需谨慎判断。
警惕自动扩展与碎片化干扰判断
表空间爆满不等于“某张表太大”,也可能是小表反复 DELETE/INSERT 导致高水位不降,或数据文件开启 AUTOEXTENSIBLE 但磁盘已满。所以查出 TOP 10 后,务必同步检查:
- 目标表空间的数据文件是否启用自动扩展:
SELECT file_name, autoextensible, maxbytes FROM dba_data_files WHERE tablespace_name = 'USERS' - 该表空间真实剩余空间:
SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024) free_mb FROM dba_free_space WHERE tablespace_name = 'USERS' GROUP BY tablespace_name - 是否存在大量未释放的临时段或 UNDO:查
v$sort_segment和v$undostat,排除临时操作干扰
真正难定位的,往往不是最大的那张表,而是几十张中等大小的表集体膨胀,或者一张表的 LOB 段和索引段分散在不同查询里,没人把它们算到一起。











