根本原因是表未启用行移动(需先执行ALTER TABLE t ENABLE ROW MOVEMENT),或存在不可收缩对象(如函数索引、域索引、未单独处理的IOT溢出段、DISABLE STORAGE IN ROW的LOB等);shrink需物理挪动行并下调HWM,非简单碎片清理。
Oracle 中 ALTER TABLE ... SHRINK SPACE CASCADE 为什么没效果?
根本原因通常是表没启用行移动(row movement),或者存在不可收缩的依赖对象(比如基于函数的索引、域索引、iot 的溢出段未单独处理)。shrink 是高水位线(hwm)下移操作,不是简单清理碎片;它需要物理挪动行、重写块、更新索引,前提是你允许 oracle 搬动数据行。
- 执行前必须先开启:
ALTER TABLE t ENABLE ROW MOVEMENT,否则报错ORA-10636: row movement is not enabled -
CASCADE会顺带 shrink 主键/唯一约束对应的索引,但不会处理普通非唯一索引、函数索引、位图索引——这些得手动 shrink 或重建 - 如果表有
LONG、LOB列(尤其是DISABLE STORAGE IN ROW的CLOB),shrink 可能失败或跳过 LOB 段,需单独对LOB执行SHRINK SPACE
Shrink 前必须检查的三个状态
不是所有表都适合直接 shrink。Oracle 会在某些结构下静默跳过空闲空间回收,比如分区表的单个分区没显式指定、或启用了压缩但压缩类型不支持在线 shrink(如 COMPRESS FOR OLTP 支持,COMPRESS FOR QUERY HIGH 不支持)。
- 查是否启用了行移动:
SELECT table_name, row_movement FROM user_tables WHERE table_name = 'T' - 查是否有不可 shrink 的索引:
SELECT index_name, index_type FROM user_indexes WHERE table_name = 'T' AND index_type NOT IN ('NORMAL', 'UNIQUE') - 查 LOB 段状态:
SELECT column_name, segment_name, cache FROM user_lobs WHERE table_name = 'T';若cache = 'NO',shrink 可能不生效,建议先ALTER TABLE t MODIFY LOB(col) (CACHE)
SHRINK SPACE COMPACT 和 SHRINK SPACE 的关键区别
前者只整理块内空间、把行往高地址挤,不动 HWM;后者在 compact 基础上还下调 HWM,并释放空间给表空间重用。线上业务怕锁表太久,常先用 COMPACT,再择机执行完整 shrink。
-
SHRINK SPACE COMPACT允许并发 DML(需已开ROW MOVEMENT),但会持有SS(sub-share)锁,阻塞 DDL(如加列、建索引) -
SHRINK SPACE需要短暂持有EXCLUSIVE锁(几秒到几分钟不等),期间表不可 DML;如果中间被中断,下次 shrink 仍可继续,但需确保没残留的SHRINK事务(查v$session_longops) - 两者都不支持在 Flashback Data Archive 启用的表上运行,会报
ORA-13525: operation not allowed on flashback archive enabled table
真正释放空间后,为什么 dba_segments.bytes 没变?
因为 shrink 调整的是段(segment)内部的高水位,但 Oracle 不会自动把腾出的 extent 归还给表空间——除非你后续执行 ALTER TABLE ... DEALLOCATE UNUSED,或者 shrink 时加上 KEEP 子句控制保留多少空间。
- 执行完 shrink 后,立即查:
SELECT bytes/1024/1024 mb FROM dba_segments WHERE segment_name = 'T',大概率和 shrink 前一样 - 运行
ALTER TABLE t DEALLOCATE UNUSED KEEP 0M才会把 HWM 下方所有空闲 extent 返还给表空间(注意:这步也需ROW MOVEMENT开启) - 如果表空间是
ASSM(自动段空间管理),返还后的空间会被其他对象复用;但如果是MANUAL管理,还得手工 coalesce freelist groups
shrink 的本质是“重排+标记可回收”,不是“删块”。很多人卡在这一步,以为 shrink 完就该看到磁盘空间下降,其实还差一次显式的 deallocation。










