oracle表空间碎片本质是空闲区分散不连续,真正反映碎片程度的是空闲区能否被有效利用;需通过dba_free_space分析空闲区分布,并用dbms_space.space_usage或object_space_usage评估段级逻辑碎片。

Oracle表空间碎片不是“清空回收站”就能解决的问题,它本质是数据块在物理存储上离散分布导致的IO放大和空间浪费。直接执行ALTER TABLE ... SHRINK SPACE往往失败或无效——关键在于你得先确认碎片是否真在表段里,还是藏在表空间层面;更要分清:你要释放空间给其他对象用,还是只优化单表扫描性能。
怎么判断表空间真有碎片,而不是误报?
查DBA_FREE_SPACE只能看“空闲块总数”,但碎片核心是“空闲块太小、太散”。真正有效的判断方式是看连续空闲块的最大尺寸:
- 执行
SELECT tablespace_name, MAX(bytes) / 1024 / 1024 max_free_mb FROM dba_free_space GROUP BY tablespace_name,如果最大连续空闲块 - 对比
dba_tablespaces里的block_size和extent_management:如果是LOCAL管理+AUTO段管理,但max_free_mb远小于block_size * 16(即一个区最小大小),说明区分配已卡死 - 不要只看
free_mb总和——表空间显示还有20GB空闲,但最大连续块只有64KB,新插入大对象就会报ORA-01652: unable to extend temp segment
ALTER TABLE ... SHRINK SPACE 失败的三个常见原因
这个命令看似简单,但失败时往往报错模糊。最常踩的坑不是语法,而是前置状态没校验:
-
ORA-10631: SHRINK clause not allowed for this object:表上有位图索引、函数索引、域索引,或启用了物化视图日志——这些对象不支持在线收缩,必须先删或禁用 -
ORA-10636: ROW MOVEMENT is disabled:即使你记得ENABLE ROW MOVEMENT,也要确认不是在子分区级别漏设(对分区表,需逐个子分区启用) - 执行后
blocks没变、empty_blocks仍为0:说明表所在表空间不是AUTO段管理,或者用了UNIFORM区大小且当前区尺寸过大,导致收缩后无法归还空间
表空间级碎片必须用 ALTER TABLESPACE ... COALESCE 吗?
不推荐盲目执行ALTER TABLESPACE ... COALESCE。它只合并相邻的空闲区,对分散的小碎片无效,且在高并发写入时可能引发锁争用。更稳妥的做法是:
- 先定位“罪魁表”:用
SELECT owner, segment_name, segment_type, bytes/1024/1024 mb FROM dba_segments WHERE tablespace_name = 'YOUR_TS' ORDER BY bytes DESC找出占用前5的大段 - 对这些大表逐个执行
SHRINK SPACE COMPACT(不释放空间但整理内部碎片),再跟SHRINK SPACE——比直接MOVE风险低得多 - 如果表空间里大量小对象(如
LOB段、INDEX),优先重建索引:ALTER INDEX ... REBUILD ONLINE,因为索引碎片对查询影响比表本身更直接
分区表碎片整理要避开的致命陷阱
对千万级以上的分区表(比如按天/月分区的日志表),SHRINK SPACE会逐个扫描所有分区,耗时极长且锁表时间不可控。正确做法是:
- 只对已归档、只读的旧分区操作:
ALTER TABLE t PARTITION p_202501 SHRINK SPACE,新分区保持原样 - 禁止对全局索引执行
SHRINK——它会失效整个索引,必须配合UPDATE GLOBAL INDEXES参数,否则DDL后索引变成UNUSABLE - 如果分区数超1000个,
DBMS_REDEFINITION比SHRINK更可靠,但要注意临时表空间必须足够大,否则ORA-01652会发生在重定义过程里
碎片整理不是“一键优化”,而是精确外科手术:表级收缩解决HWM问题,表空间级整理依赖底层区管理机制,而分区表必须按生命周期分层处理。最容易被忽略的是——做完所有操作后,必须立刻执行DBMS_STATS.GATHER_TABLE_STATS,否则优化器仍按旧统计信息生成执行计划,等于白干。











