应聚合dba_segments按segment_type分组识别物理碎片,而非仅按大小排序;需结合types字段判断TABLE/LOBSEGMENT/INDEX混杂情况,并通过HWM与实际数据量比值评估空间利用率。

直接看 dba_segments 聚合 + segment_type 构成,比单看名字或大小更可靠。
查表空间中物理分散最狠的段(含类型混杂识别)
只按大小排序会误判——比如一个 SYS_LOB000012345C00002$$ 占 2GB,但实际属于某张小表的 CLOB 字段;又或者 SALE_IDX 名字像索引,却和基表共用段名。必须把 segment_type 拉进来聚合:
SELECT s.owner, s.segment_name,
ROUND(SUM(s.bytes)/1024/1024) size_mb,
LISTAGG(DISTINCT s.segment_type,',') WITHIN GROUP (ORDER BY s.segment_type) types
FROM dba_segments s
WHERE s.tablespace_name = 'USERS'
GROUP BY s.owner, s.segment_name
ORDER BY size_mb DESC
FETCH FIRST 10 ROWS ONLY;
-
types显示TABLE,LOBSEGMENT,INDEX:说明这张逻辑表在物理上被拆成三类段,高碎片风险;TABLE段本身可能才几十 MB,但总压力来自 LOB 和索引 - Oracle 11g 及更早不支持
FETCH FIRST,得套ROWNUM,但外层ORDER BY必须存在,否则排序失效 - 如果 TOP 10 里
types大量是LOBSEGMENT或INDEX,别急着MOVE基表——先查dba_lobs或dba_indexes回溯真实归属
盯紧高水位线与空间利用率落差大的表
表段是否真碎片,关键不是“占多少空间”,而是“用了多少”。blocks 是高水位线(HWM)以下已分配块数,num_rows * avg_row_len 是理论数据体积,两者比值低就说明空洞多:
SELECT owner, table_name,
ROUND(blocks * 8 / 1024, 2) "allocated MB",
ROUND(num_rows * avg_row_len / 1024 / 1024, 2) "used MB",
ROUND((blocks * 8 - num_rows * avg_row_len / 1024) / 1024, 2) "waste_MB",
ROUND((num_rows * avg_row_len / 1024) / NULLIF(blocks * 8, 0), 3) "util_rate"
FROM dba_tables
WHERE blocks IS NOT NULL
AND num_rows > 0
AND (num_rows * avg_row_len / 1024) / NULLIF(blocks * 8, 0)
util_rate 是硬指标:不到 30% 空间被真正利用,基本可判定碎片严重-
empty_blocks字段不可信——它只在ANALYZE TABLE ... ESTIMATE/COMPUTE STATISTICS后才更新,且 Oracle 10g+ 默认不收集,慎用 - 注意
avg_row_len来自统计信息,若长期未收集(DBMS_STATS.GATHER_TABLE_STATS),结果偏差大
别漏掉分区索引的局部碎片
全局索引碎片容易发现,但局部索引(LOCAL)的碎片藏在每个分区里。直接对索引名执行 ANALYZE INDEX ... VALIDATE STRUCTURE 会报 ORA-02243,必须拆到分区粒度:
- 先查分区结构:
SELECT index_name, partition_name, high_value FROM dba_ind_partitions WHERE index_name = 'IND_OBJ_ID' - 再逐个分析:
ANALYZE INDEX SCOTT.IND_OBJ_ID PARTITION(SYS_P123) VALIDATE STRUCTURE - 每次分析后立刻查
index_stats,算ROUND(del_lf_rows / NULLIF(lf_rows, 0) * 100, 2)——跨分区或跨会话就丢数据 - 局部索引每个分区是独立段,
del_lf_rows / lf_rows > 20%的分区才值得单独REBUILD PARTITION,不用整索引重建
真正难处理的不是单个大段,而是那些 types 混杂、util_rate 低、又带大量分区的组合体。这时候光看 SQL 输出不够,得顺着 segment_name 去 dba_lobs、dba_ind_partitions、dba_tab_partitions 一层层翻归属关系——名字只是标签,物理段才是真相。











