delete 不降低高水位线(hwm),故表空间占用不变;shrink space 需先启用行移动,再分步收缩以降低 hwm;最后需 resize 数据文件才能释放操作系统级磁盘空间。

DELETE 不会降低高水位线(HWM),表空间占用自然不会减少——这不是故障,是 Oracle 的段管理机制决定的。
为什么 DELETE 后 BLOCKS 字段不变、全表扫描还那么慢
Oracle 表的物理空间由段(segment)管理,HWM 是这个段里“用过最高位置”的标记。只要发生过 INSERT,HWM 就会上升;但 DELETE 只是把行标为“已删除”,块仍归属该段,HWM 一动不动。
后果很直接:
-
SELECT COUNT(*) FROM your_table很快,但SELECT * FROM your_table全表扫描仍要读到旧 HWM 位置,IO 没省 -
DBA_SEGMENTS.BYTES和USER_TABLES.BLOCKS都不下降,监控看到的“已用空间”纹丝不动 - 即使
NUM_ROWS = 0,BLOCKS还是几万,说明大量块处于“空但不可重用”状态
怎么确认是不是 HWM 虚高导致的空间滞留
别只查 DBA_FREE_SPACE 或看 OEM 图表——那反映的是表空间级空闲,不是单表真实水位。必须查块级统计:
先确保统计准确:EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME')
再执行:SELECT blocks, empty_blocks, num_rows FROM user_tables WHERE table_name = 'TABLE_NAME'
关键判断逻辑:
- 如果
num_rows极小(比如 100),但blocks仍为 50000+ → HWM 明显虚高 -
empty_blocks值越大,说明 HWM 下“逻辑空块”越多,空间浪费越严重 - 注意:
ANALYZE TABLE ... ESTIMATE STATISTICS已过时,优先用DBMS_STATS
SHRINK SPACE 要开 ROW MOVEMENT,不开就报 ORA-10636
SHRINK SPACE 是在线收缩 HWM 的唯一可控方式,但它不是“开箱即用”:
必须分步操作:
- 先启用行移动:
ALTER TABLE your_table ENABLE ROW MOVEMENT - 再执行收缩:
ALTER TABLE your_table SHRINK SPACE COMPACT(只整理、不降 HWM,DML 可并发) - 最后真正降 HWM:
ALTER TABLE your_table SHRINK SPACE(短暂加 X 锁,所有 DML 阻塞) - 加
CASCADE参数可连带收缩索引:SHRINK SPACE CASCADE
收完记得关掉:ALTER TABLE your_table DISABLE ROW MOVEMENT,否则可能干扰依赖 ROWID 的应用逻辑(如某些物化视图或自定义审计)。
收缩完磁盘文件还是没变小?那是你漏了 RESIZE
SHRINK SPACE 只回收段内空间,让块归还给表空间;它不碰数据文件(.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/your.dbf' RESIZE 2000M - 注意:
RESIZE不能跨 extent 边界,若报ORA-03297,说明你要缩到的位置后面还有数据块——得先SHRINK更彻底,或换用MOVE+REBUILD方案
最易被忽略的一点:HWM 降了 ≠ 磁盘释放了。从 DELETE 到真正腾出操作系统级空间,中间至少要走三步——收集统计、SHRINK、RESIZE,少一步,监控数字就不动。











