delete不释放空间是oracle正常设计,因仅逻辑删除且hwm不降;需用truncate重置hwm、shrink space(先启row movement)降hwm,或手动resize数据文件才能真正释放磁盘空间。
delete 不释放空间,这是 oracle 的正常行为,不是你删错了,也不是数据库坏了。
Oracle 表的高水位线(HWM)不会因 DELETE 下降。已删除行占用的数据块仍被标记为“已使用”,后续 INSERT 会优先复用这些块,但 DBA 看到的表空间使用率(比如从 DBA_SEGMENTS 或 DBA_FREE_SPACE 计算)不会变——因为段大小没缩,空闲空间也没退回给表空间。
DELETE 后空间不释放的典型表现
- 执行
DELETE FROM big_table WHERE create_time 删除 80% 数据 - 查询
DBA_SEGMENTS中该表的bytes字段,数值完全不变 - 表空间总使用率(
DBA_TABLESPACES+DBA_DATA_FILES)也无下降 - 全表扫描性能反而更差:Oracle 仍需遍历 HWM 下所有块,包括大量空块
这说明:
-
DELETE只是逻辑删除,不移动 HWM - 空间仍被该表段独占,其他对象无法复用
- 索引叶块也可能残留大量“标记删除”条目,进一步拖慢查询
什么情况下该用 TRUNCATE 而不是 DELETE
TRUNCATE 是 DDL 操作,直接重置 HWM 并释放空间(除非加 REUSE STORAGE):
- 要清空整张表,且不需要回滚或触发器响应
- 表上没有启用
FLASHBACK ARCHIVE(否则报错ORA-55610) - 没有外键被其他表引用(否则需先禁用约束)
示例:
TRUNCATE TABLE log_history DROP STORAGE;
注意:DROP STORAGE 是默认行为,显式写出更清晰;若想保留初始区段(比如预分配),改用 REUSE STORAGE。
SHRINK SPACE 适用场景与关键前提
对已 DELETE 大量数据的表,用 SHRINK 是最常用的空间回收方式,但它有硬性依赖:
- 表必须启用行迁移:
ALTER TABLE t ENABLE ROW MOVEMENT - 表不能有基于函数的索引、域索引、IOT 的映射表等受限对象
- 执行
SHRINK SPACE会短暂锁表(DML 阻塞),而SHRINK SPACE COMPACT可在线做,但不降 HWM - 索引需要单独收缩:
ALTER INDEX idx_name SHRINK SPACE,否则索引段仍占空间
常见误操作:
- 忘记开
ROW MOVEMENT,执行SHRINK直接报错ORA-10636 - 只收缩表,不收缩索引,结果表段小了,索引段反而更大
- 在归档模式下未开启补充日志(
ADD SUPPLEMENTAL LOG DATA),导致某些复制场景失败
临时表空间和永久表空间要分开处理
别把 TEMP 和普通表空间混为一谈。ALTER TABLESPACE temp SHRINK SPACE 在 Oracle 19c 中语法合法但完全无效——文档明确写死只支持永久表空间。临时文件(tempfile)扩展后不会自动收缩,v$temp_space_header 才是判断真实空闲的唯一依据。
如果你看到 DBA_TEMP_FILES.bytes 还剩 20GB,但频繁报 ORA-01652,说明实际可分配 extent 已耗尽。此时唯一解法是重建临时表空间,顺序不能错:新建 → 切换默认 → 确认无活跃会话 → 删除旧表空间。
真正容易被忽略的是:BLOB/LOB 字段长期持有临时段,即使主事务结束,其排序或 hash 操作残留的临时段可能持续数小时。查 v$sort_usage 和 v$tempseg_usage 比看 DBA_TEMP_FILES 更准。











