DELETE不释放空间是因为仅标记行删除而不降低HWM;TRUNCATE DROP STORAGE可重置HWM但无条件;SHRINK SPACE需启用行迁移,COMPACT不降HWM,SHRINK降HWM但阻塞DML,CASCADE连带收缩索引。
DELETE 不释放空间,是因为 HWM 没动
oracle 中执行 delete 只是把行标记为“已删除”,数据块仍保留在高水位线(hwm)之下,后续全表扫描、索引范围扫描仍需遍历这些块。hwm 就像水库的最高水位标记——水退了,但水泥墙没拆,库容没变。
常见错误现象:SELECT bytes/1024/1024 FROM dba_segments WHERE segment_name = 'TBL_LOG'; 查出来的大小和删前几乎一样;DBA_FREE_SPACE 里对应表空间的空闲空间也没明显增长。
- HWM 位置由
dba_tables.blocks和dba_tables.empty_blocks共同体现,不是靠行数判断 -
DELETE后立刻查dba_extents,你会发现 extent 数量、起始块号都没变 - 即使删光全表,HWM 也不会自动下降——除非显式收缩或重建
TRUNCATE DROP STORAGE 能降 HWM,但不能带条件
TRUNCATE TABLE t DROP STORAGE 是真正重置 HWM 的方式:它直接释放所有 extent,把段(segment)缩回到初始状态。但注意,它不支持 WHERE 条件,是“全有或全无”操作。
使用场景:你确定只要保留极少量数据(比如只留最近 7 天),且能接受短暂停服或双表切换流程。
- 先用
CREATE TABLE t_bak AS SELECT * FROM t WHERE ...抽出要留的数据 - 再执行
TRUNCATE TABLE t DROP STORAGE - 最后
INSERT /*+ APPEND */ INTO t SELECT * FROM t_bak,并COMMIT - 别漏掉
ANALYZE TABLE t COMPUTE STATISTICS或DBMS_STATS.GATHER_TABLE_STATS更新统计信息
SHRINK SPACE 要开行迁移,且锁行为差异大
ALTER TABLE t SHRINK SPACE 是在线收缩 HWM 的主流方式,但它要求先启用行迁移:ALTER TABLE t ENABLE ROW MOVEMENT。否则报错 ORA-10636: row movement is not enabled。
三个常用变体的行为区别必须清楚:
-
SHRINK SPACE COMPACT:只整理块内空间,不降 HWM,允许并发 DML -
SHRINK SPACE:降 HWM,但会短暂阻塞 DML(在调整段头和 extent map 阶段) -
SHRINK SPACE CASCADE:连带收缩主表 + 所有相关索引,锁时间更长,适合维护窗口期执行
性能影响:对几百 GB 表,SHRINK SPACE 可能持续几十分钟;期间 V$SESSION_LONGOPS 能查到 shrink table 进度。
索引也要 shrink,否则空间浪费照旧
删数据后,索引叶块里大量键值变成“逻辑删除”,但索引段本身不会自动压缩。即使表 shrunk 成功,索引仍占着老空间,甚至可能因分裂变得更臃肿。
必须单独处理索引:
- 查哪些索引需要收缩:
SELECT index_name FROM user_indexes WHERE table_name = 'T' - 逐个执行:
ALTER INDEX idx_log_time SHRINK SPACE - 如果索引是函数索引或基于域的索引,
SHRINK不支持,得用REBUILD - 重建索引时加
ONLINE(如ALTER INDEX idx REBUILD ONLINE)可减少锁表时间
最容易被忽略的是:SHRINK TABLE 默认不碰索引,哪怕加了 CASCADE,某些分区索引或不可用索引也不会被处理,得人工确认。


















