delete 不动 hwm 因为仅标记行删除而不回收块,hwm 作为已分配空间边界不会自动下降;全表扫描仍遍历至原 hwm,导致性能下降。

DELETE 为什么不动 HWM
因为 DELETE 只是把行标记为“已删除”,不回收数据块,也不调整段(segment)的物理边界。Oracle 仍认为这些块属于该表——高水位线(HWM)就是这个“已分配但未必有数据”的边界标记。哪怕你删光了整张表,SELECT COUNT(*) 返回 0,HWM 还停在原来的位置,全表扫描照样扫到那儿。
常见错误现象:
- 表里只剩几千行,
SELECT *却慢得像在读百万行 -
DBA_SEGMENTS.BYTES和USER_TABLES.BLOCKS没变,明明刚删掉 95% 数据 -
EMPTY_BLOCKS字段值很大,说明 HWM 上方有大量从未用过的块,但 HWM 本身没动
SHRINK SPACE 能降 HWM 吗?前提是什么
能,但不是无条件生效。ALTER TABLE ... SHRINK SPACE 的本质是:把 HWM 下方有数据的块“向上挪”,腾出底部空间,再把 HWM 往下压。这个过程要求表支持行移动。
必须先执行:
ALTER TABLE <table_name> ENABLE ROW MOVEMENT;</table_name>
否则会报错:ORA-10636: ROW MOVEMENT is not enabled
还要注意:
- 表不能有基于函数的索引、域索引、位图连接索引(这些在 shrink 后可能失效)
- 不能在 NOLOGGING 模式下 shrink(否则备份链断裂)
- shrink 会产生大量 UNDO 和 REDO,大表建议在低峰期做,并确认归档空间充足
- shrink 不会自动收缩数据文件(
DATAFILE),只是降低段内 HWM;要缩文件还得配ALTER DATABASE DATAFILE ... RESIZE
SHRINK SPACE 和 MOVE 的关键区别
两者都能降 HWM,但行为不同:
-
ALTER TABLE ... MOVE:彻底重写整个段,HWM 归零,但所有索引全部失效,必须手动ALTER INDEX ... REBUILD -
ALTER TABLE ... SHRINK SPACE:在线操作(可加CASCADE级联 shrink 索引),索引保持有效,但需要行移动启用,且不能在并行 DML 期间执行
性能影响上,MOVE 通常更快(单次重写),SHRINK 更可控(可分步:SHRINK SPACE COMPACT 先整理再 SHRINK SPACE 降 HWM),但锁持有时间略长。
别忘了验证和监控
shrink 完不等于万事大吉。必须查两件事:
- 确认 HWM 是否真降了:
SELECT BLOCKS, EMPTY_BLOCKS FROM USER_TABLES WHERE TABLE_NAME = '<your_table>';</your_table>——BLOCKS应明显减少,EMPTY_BLOCKS可能变负(说明 HWM 已低于初始分配点) - 检查索引是否可用:
SELECT INDEX_NAME, STATUS FROM USER_INDEXES WHERE TABLE_NAME = '<your_table>';</your_table>—— status 必须是VALID - 如果用了
SHRINK SPACE CASCADE,还得看子对象:SELECT SEGMENT_NAME, SEGMENT_TYPE FROM USER_SEGMENTS WHERE SEGMENT_NAME IN (SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME = '<your_table>');</your_table>
HWM 是段级元数据,不落盘就看不见变化;很多 DBA 在 shrink 后没 gather stats,导致执行计划仍按旧统计量走,性能问题照旧。别跳过 DBMS_STATS.GATHER_TABLE_STATS 这一步。











