MOVE 不能恢复 NOCOMPRESS 表的实际空间,因其仅重写数据而不改变压缩属性、不降低HWM、不处理LOB段、不触发空间回收;需配合 DEALLOCATE UNUSED 或 SHRINK SPACE 才能释放空间。
为什么 MOVE 不能恢复 NOCOMPRESS 表的实际空间?
因为 move 操作本身不改变段(segment)的压缩属性,它只是把数据按当前表定义重新写入新区段——如果原表是 nocompress,move 后仍是 nocompress,不会“自动压缩”或“清理空闲块”。你看到的空间没释放,往往是因为高水位线(hwm)没降,或者存在大量已删除但未回收的块。
-
MOVE不重建索引,也不触发SHRINK SPACE,所以原有空块仍被 HWM 锁定 - 如果表上有
LOB列且用了DISABLE STORAGE IN ROW,MOVE不会自动整理 LOB 段,这部分空间常被忽略 - 执行前没做
ALTER TABLE ... DEALLOCATE UNUSED,导致尾部未用区无法归还给表空间
DEALLOCATE UNUSED 和 SHRINK SPACE 怎么选?
两者都能降 HWM,但前提和副作用完全不同:DEALLOCATE UNUSED 只释放 HWM 之后的空区,快、无锁、不移动数据;SHRINK SPACE 能把 HWM 往下拉,但要求表启用行移动(ENABLE ROW MOVEMENT),且会触发全表扫描+块重组,期间可能阻塞 DML。
- 先试
ALTER TABLE t MOVE→ 再立刻跟ALTER TABLE t DEALLOCATE UNUSED,这是最安全的组合 - 想真正压低 HWM?必须加
SHRINK SPACE COMPACT(先整理再收缩),否则SHRINK SPACE可能报错ORA-10636: ROW MOVEMENT is not enabled -
SHRINK SPACE对全局临时表、IOT 主键表、有函数索引的表不支持,容易静默失败
压缩状态误判:NOCOMPRESS 不等于“没压缩过”
很多人查 USER_TABLES.COMPRESSION 是 DISABLED 就以为这表从没压过,其实可能是早期用过 BASIC 压缩,后来 MOVE 时没显式指定 NOCOMPRESS,结果继承了旧压缩设置——Oracle 默认沿用原压缩类型,除非你明确写出来。
- 建表时没写
COMPRESS或NOCOMPRESS,行为取决于表空间默认压缩策略(DEFERRED或NONE) -
MOVE时不加NOCOMPRESS,语句实际等价于MOVE COMPRESS BASIC(如果原表是 BASIC) - 查真实压缩效果,别只看
COMPRESSION字段,用DBMS_SPACE.SPACE_USAGE看实际块填充率更准
LOB 段空间不释放的典型漏点
MOVE 表时,LOB 列默认不跟着一起移动。即使主表数据重写了,LOB 段仍留在原位置,且不会自动 SHRINK,这是空间“失踪”的最大黑盒。
- 必须单独处理:对每个 LOB 列执行
ALTER TABLE t MODIFY LOB (c) (SHRINK SPACE) - 如果 LOB 是
DISABLE STORAGE IN ROW,还要确认对应 LOB 表空间有足够空闲区,否则SHRINK会卡住 - LOB 的
CHUNK大小影响实际压缩率,SELECT CHUNK FROM USER_LOBS要核对是否和表空间 block size 匹配
DEALLOCATE UNUSED 这两步,不是 MOVE 不起作用,而是它只干了一半活。










