DELETE 不会降低HWM,导致空间未真正释放;需通过DBMS_STATS收集统计并查询BLOCKS与NUM_ROWS判断HWM虚高;TRUNCATE可重置HWM但不可回滚;SHRINK SPACE需先启用行移动并注意锁影响;最后须手动RESIZE数据文件才能释放磁盘空间。
delete 不会降 hwm,所以空间“看着空了”,oracle 还是当它在用。 表里删光数据,blocks 字段不变,全表扫描照旧扫到老位置,磁盘文件大小也纹丝不动——这不是 bug,是 oracle 的设计逻辑。
怎么确认是不是 HWM 卡住了空间?
别只看 DBA_SEGMENTS.BYTES 或表空间剩余率。真实水位得查块级统计:
- 先收集准确统计:
EXEC DBMS_STATS.GATHER_TABLE_STATS('YOUR_SCHEMA', 'YOUR_TABLE')(比ANALYZE TABLE更稳) - 再查关键字段:
SELECT blocks, empty_blocks, num_rows FROM user_tables WHERE table_name = 'YOUR_TABLE' -
blocks就是当前 HWM 对应的总块数;empty_blocks是 HWM 下明确为空的块;差值越大,说明“幽灵空间”越多 - 如果
num_rows很小但blocks仍高达几万,基本可以断定 HWM 虚高
TRUNCATE 能重置 HWM,但你真能用吗?
TRUNCATE TABLE your_table DROP STORAGE 是最干脆的解法:HWM 归零、空间立即释放、段收缩到位。但它有硬约束:
- 不可回滚,且事务一提交就生效
- 表不能被任何外键引用,否则报
ORA-02266 - 如果有 Flashback Data Archive 启用,直接报错
- 即使成功,数据文件本身仍不会缩小——还得手动
ALTER DATABASE DATAFILE ... RESIZE
在线收缩 SHRINK SPACE:要开行移动,也得锁表
业务不能停?那就走 SHRINK SPACE,但它不是无感操作:
- 必须先执行:
ALTER TABLE your_table ENABLE ROW MOVEMENT(否则报ORA-10636) -
SHRINK SPACE COMPACT只整理碎片、不降 HWM,期间加 RX 锁,影响小 -
SHRINK SPACE才真正移动 HWM,第二阶段需短暂 X 锁,所有 DML 阻塞 - 带
CASCADE参数可连索引一起收缩,避免后续手动REBUILD - 执行完记得
DISABLE ROW MOVEMENT,否则可能干扰依赖 rowid 的逻辑
收缩完了,磁盘空间还是没回来?
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报错 “cannot resize to smaller size”,说明空闲空间不连续,得先 shrink 表或 move 段
真正卡住空间的从来不是 DELETE 本身,而是 HWM 这根“历史水位标尺”没动。收缩操作里最易被忽略的,是 ENABLE ROW MOVEMENT 和 RESIZE 这两步——缺一不可,且顺序不能颠倒。











