执行 SHRINK SPACE 前必须满足三个硬性条件:表已启用行移动(ENABLE ROW MOVEMENT)、所在表空间段空间管理为AUTO、表类型为堆表;缺一不可,否则报错或静默失败。
执行 SHRINK SPACE 前必须检查的三个硬性条件
不满足任一条件,alter table t shrink space 要么报错(如 ora-10636、ora-10637),要么静默失败(dba_segments.bytes 不变、hwm 不动)。
- 表空间必须是本地管理:
SELECT extent_management FROM dba_tablespaces WHERE tablespace_name = 'TBS_NAME'→ 结果必须为'LOCAL' - 段空间管理必须为自动(ASSM):
SELECT segment_space_management FROM dba_tablespaces WHERE tablespace_name = 'TBS_NAME'→ 必须返回'AUTO' - 表必须已启用行移动:
ALTER TABLE t ENABLE ROW MOVEMENT—— 没执行过就收缩,命令不报错但无效
常见踩坑:误以为只要表是普通堆表就能 shrink;实际上 LONG、BFILE、函数索引、位图连接索引所在的表,Oracle 明确禁止 shrink,且不提示具体原因。
SHRINK SPACE COMPACT 和 SHRINK SPACE 的锁行为差异
二者不是“先 compact 再 shrink”才完整,而是两个独立策略:前者只做数据重组,后者才真正释放空间。选错会卡业务。
-
SHRINK SPACE COMPACT:在表上加 RX 锁(行级共享锁),仅移动行、填充空块,不调 HWM,DML 基本不受影响;适合大表、高峰期先整理 -
SHRINK SPACE(无后缀):需瞬时 X 锁(排他锁),阻塞所有 DML,但完成后立即释放空间;适合维护窗口内快速收尾 -
SHRINK SPACE CASCADE:在上一条基础上连带收缩所有常规索引段;但对 SECUREFILE LOB、分区表的 LOB 列完全无效
注意:COMPACT 后查 DBA_TABLES.BLOCKS 会变小,但 DBA_SEGMENTS.BYTES 不变 —— 空间还没真还给表空间。
分区表和 LOB 段必须单独处理
直接对分区表执行 SHRINK SPACE 默认只作用于整个表段,不会触及其分区或 LOB 子段;而 CASCADE 也跳过 LOB,极易误判“已收缩成功”。
- 收缩单个分区:
ALTER TABLE t MODIFY PARTITION p202406 SHRINK SPACE CASCADE - SECUREFILE LOB 必须显式收缩:
ALTER TABLE t MODIFY LOB (col_lob) (SHRINK SPACE) - BASICFILE LOB 在 19c 完全不支持 shrink,只能用
MOVE PARTITION+REBUILD INDEX,且需额外空间
执行完任何 shrink 操作后,务必手动收集统计信息:DBMS_STATS.GATHER_TABLE_STATS,否则优化器仍按旧 BLOCKS 和 NUM_ROWS 估算执行计划,可能引发性能倒退。
怎么确认 shrink 真的成功了?别信 DBA_SEGMENTS
DBA_SEGMENTS.BYTES 和 v$segment_statistics 不反映实时 HWM 变化,容易误判。真正有效的验证方式只有两种:
- 对比收缩前后:
SELECT segment_name, blocks FROM dba_segments WHERE segment_name = 'T'——BLOCKS值变小才算生效 - 查表级块使用率:
SELECT blocks, empty_blocks FROM dba_tables WHERE table_name = 'T',HWM 下移后empty_blocks应显著减少
ANALYZE TABLE COMPUTE STATISTICS 虽能更新 EMPTY_BLOCKS,但已在 19c 中弃用,生产环境禁用;更稳妥的是用 DBMS_SPACE.SPACE_USAGE 查 SECUREFILE LOB 的实际使用情况。











