确认hwm拖慢性能需查dba_tables.blocks与估算已用块数的差距,差值大或waste_per>70%即为问题;shrink space失败主因是表空间非assm、未启用行移动或存在不支持索引;move锁表但彻底,shrink分阶段更灵活;truncate需加drop storage才重置hwm,且须注意依赖和统计信息更新。

高水位线(HWM)不会因 DELETE 自动下降,全表扫描仍要读取 HWM 以下所有块——哪怕里面全是空的。必须主动干预才能降。
怎么确认表真的被高水位拖慢了?
不能只看 num_rows 小就以为没问题。关键要看实际使用块数和分配块数的差距:
- 查
dba_tables.blocks(已分配块数)和avg_used_blocks(估算已用块数),差值大说明碎片严重 - 用脚本算
waste_per(空间浪费百分比),>70% 就该处理了 - 执行
SELECT COUNT(*) FROM table_name耗时明显长于同量级其他表,且执行计划显示全表扫描(FULL),基本可锁定 HWM 问题
ALTER TABLE ... SHRINK SPACE 为什么常失败?
这个命令看着最“无感”,但依赖三个硬性前提,缺一不可:
- 表所在表空间必须是
AUTOSEGMENT SPACE MANAGEMENT (ASSM),MSSM 不支持 shrink - 必须先执行
ALTER TABLE table_name ENABLE ROW MOVEMENT,否则报ORA-10636: row movement is not enabled - 不能有基于函数的索引、域索引、BITMAP JOIN 索引,否则报
ORA-10631: SHRINK clause should not be specified for this object
常见误操作:只做 SHRINK SPACE COMPACT(只整理数据不降 HWM),忘了后续的 SHRINK SPACE;或没关行移动就直接 shrink,结果命令静默跳过。
MOVE 和 SHRINK 到底选哪个?
不是性能快慢的问题,是业务连续性与资源代价的权衡:
-
MOVE:全程锁表(DML 阻塞),索引全部失效,必须立刻REBUILD,适合维护窗口期明确的场景 -
SHRINK:分两步,COMPACT阶段可在线(DML 不阻),SHRINK SPACE阶段才需短暂锁表;但会产生大量 UNDO/REDO,对归档压力大 - 如果表带
LOB字段,SHRINK默认不处理 LOB 段,得加SHRINK SPACE CASCADE;而MOVE必须显式指定LOB子句,否则报错
为什么 TRUNCATE 有时也不能清 HWM?
因为默认 TRUNCATE TABLE t REUSE STORAGE —— 它只是清数据,不释放段空间。真正能归零 HWM 的是:
-
TRUNCATE TABLE t DROP STORAGE(Oracle 12c+ 默认行为,旧版本需显式写) - 但注意:如果表被其他对象(如物化视图日志、外键引用)依赖,
TRUNCATE会直接报错,此时只能退回到SHRINK或MOVE - 另外,
TRUNCATE是 DDL,无法回滚,且会清空所有统计信息,后续必须手动DBMS_STATS.GATHER_TABLE_STATS
真正容易被忽略的是:降完 HWM 后,不收集统计信息,优化器依然按旧的 blocks 值估算成本,全表扫描代价还是虚高——这一步不是可选项,是必选项。











