DELETE不会释放空间是Oracle设计行为,只标记删除不降HWM,导致全表扫描仍遍历空块;需通过TRUNCATE重置HWM、SHRINK SPACE在线收缩(先ENABLE ROW MOVEMENT)、或ALTER TABLE MOVE降HWM,最后手动RESIZE数据文件才能真正释放磁盘空间。

DELETE 不会释放空间,这是 Oracle 的设计行为,不是 bug,也不是权限问题。 它只标记行删除,不移动高水位线(HWM),更不回收数据块——你看到的“空”只是逻辑空,物理上那些块仍被段(segment)锁定着。
为什么 SELECT COUNT(*) 很快但全表扫描极慢
因为 Oracle 全表扫描必须从段起始块扫到当前 HWM 位置,哪怕中间 90% 的块全是 DELETE 后留下的“幽灵块”。USER_TABLES.BLOCKS 值不变、EMPTY_BLOCKS 突然变大、NUM_ROWS 却断崖下跌,就是典型信号。
- 查真实水位别看
DBA_SEGMENTS.BYTES,它只反映已分配字节数,跟有没有数据无关 - 先跑
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME')确保统计准确 - 再查
SELECT BLOCKS, EMPTY_BLOCKS, NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'YOUR_TABLE' - 如果
NUM_ROWS是几千而BLOCKS是几十万,HWM 肯定虚高了
TRUNCATE 能重置 HWM,但你真能用吗
TRUNCATE TABLE your_table DROP STORAGE 是最干脆的解法:HWM 归零、段收缩、空间立刻可见。但它不可回滚,且有硬约束:
- 表不能被任何外键引用,否则报
ORA-02266 - 若启用了 Flashback Data Archive,直接报错
- 即使成功,
.dbf数据文件本身尺寸也不变——还得手动ALTER DATABASE DATAFILE ... RESIZE
在线收缩 SHRINK SPACE 必须开 ROW MOVEMENT
ALTER TABLE ... SHRINK SPACE 是唯一支持业务不中断的方案,但它不是无感操作:
- 必须先执行
ALTER TABLE ... ENABLE ROW MOVEMENT,否则报ORA-10636 -
SHRINK SPACE COMPACT只整理碎片、不降 HWM,期间加 RX 锁,DML 可继续 -
SHRINK SPACE才真正移动 HWM,第二阶段需短暂 X 锁,所有 DML 阻塞 - 带
CASCADE参数可连索引一起收缩,避免后续手动ALTER INDEX ... REBUILD
收缩完磁盘空间还是没回来?漏了最后一步
HWM 降了,只是段内空间回收了;操作系统看不到变化,是因为数据文件(.dbf)物理尺寸没动。这步常被跳过:
- 先确认末尾是否有连续空闲空间:
SELECT file_name, bytes/1024/1024 AS curr_mb, (bytes - NVL((SELECT SUM(bytes) FROM dba_free_space WHERE file_id = d.file_id), 0))/1024/1024 AS used_mb FROM dba_data_files d WHERE tablespace_name = 'YOUR_TS' - 只有
curr_mb - used_mb > 100(MB)以上,才值得收缩 - 执行
ALTER DATABASE DATAFILE '/path/to/file.dbf' RESIZE 5000M,注意路径和目标大小要匹配实际空闲量
很多人卡在 RESIZE 报 ORA-03297,本质是文件末尾仍有数据块分布——这不是配置错误,而是数据物理布局导致的,得先挪动数据或换用 MOVE + RESIZE 组合拳。


















