重建索引仅释放索引段空间,不缩小数据文件;dba_segments.bytes未变小是因为显示的是当前有效段占用空间,而非文件总大小,真正回收需手动清理表空间或迁移后删除。

重建索引本身不会直接回收“表空间”,它只重建索引段(index segment),释放旧索引占用的块,并在新位置分配紧凑结构;真正回收表空间,必须靠后续对索引所在表空间的清理动作——而 Oracle 不会自动收缩数据文件,ALTER INDEX ... REBUILD 完成后,旧索引段被标记为可重用,但对应的数据文件尺寸不变。
为什么 REBUILD 后 dba_segments.bytes 没变小?
这是最常被误解的一点:索引重建只是把索引从旧 segment 搬到新 segment,原 segment 立即被 drop,dba_segments.bytes 显示的是当前有效 segment 占用空间,不是数据文件总大小。你看到“没省空间”,往往是因为:
- 新索引建在同一个表空间里,而该表空间的数据文件(datafile)仍维持原尺寸,空闲块(free space)只是内部变多了,
dba_free_space会涨,但文件不缩 - 重建时没指定
TABLESPACE子句,Oracle 默认用原索引所在表空间,且可能沿用旧存储参数(如INITIAL 64K),导致新段仍分配过大 extent - 索引碎片率不高(
DEL_LF_ROWS/LF_ROWS 或height ),重建前后物理结构差异极小,bytes几乎不变
重建前必须验证的两个关键指标
盲目重建不仅无效,还浪费 I/O 和临时空间。先跑结构分析:
ANALYZE INDEX <index_name> VALIDATE STRUCTURE;</index_name>
然后在同一 session 查 index_stats:
-
height >= 4:B-tree 层数过深,可能影响范围扫描效率 -
DEL_LF_ROWS / LF_ROWS > 0.2:叶子块中 20% 以上是已删除条目,说明碎片严重 - 注意:
LF_ROWS是当前有效叶子行数,DEL_LF_ROWS是标记删除但未重用的行数;两者都来自index_stats,换 session 就查不到
重建时如何让新索引更紧凑、便于后续空间回收?
目标不是“建完就行”,而是让新索引段尽可能少占初始空间、并为表空间级清理铺路:
- 显式指定
TABLESPACE,最好选一个专用于索引、且未严重碎片化的表空间,避免和原表混用 - 加
PCTFREE 5(而非默认 10),减少预留空闲空间;大索引可考虑NOLOGGING加速(需归档模式下权衡) - 用
PARALLEL 4提速,但结束后立刻执行ALTER INDEX,否则NOPARALLEL DEGREE会永久为 4,影响后续 DML 性能 - 如果目标是彻底腾空某表空间,可先
ALTER INDEX,再删掉旧表空间(前提是确认无其他对象依赖)REBUILD TABLESPACE
重建完成后,怎样才算真正回收了空间?
索引段本身已更新,但“回收空间”这步要分两层看:
- 逻辑层回收:旧索引段立即释放,
dba_extents中消失,dba_free_space对应表空间 free_mb 上升 —— 这步FINISH_REDEF_TABLE或REBUILD完成就完成了 - 物理层回收:数据文件尺寸不变,除非你手动 shrink 表空间 —— 但注意:普通表空间不支持
SHRINK,只有启用了SEGMENT SPACE MANAGEMENT AUTO的本地管理表空间,才能对其中的段 shrink;而索引段本身不支持SHRINK SPACE,只能重建 - 所以最终能做的,是把整个表空间里的所有对象迁移走,然后
DROP TABLESPACE ... INCLUDING CONTENTS AND DATAFILES,这才是彻底释放磁盘空间
最容易被跳过的动作,是在重建前没查 v$sort_usage 和 dba_temp_free_space —— 如果临时表空间不足,REBUILD 会在 ORA-01652 中失败,而错误提示根本不会告诉你缺的是 temp 而不是 data。











