不能直接 shrink 物化视图本身,但可 shrink 其基表(含分区表)和物化视图日志(MLOG$ 表),后者需启用行移动且表空间为 ASSM;分区表应按分区 shrink,deallocate unused 不降 HWM,shrink space 才真正压缩数据并下移 HWM。
分区表物化视图能用 shrink space 吗?
不能直接 shrink 物化视图本身,但可以 shrink 它的基表(如果基表是普通表或分区表)以及它依赖的物化视图日志(mlog$ 表)。oracle 允许对物化视图日志执行 shrink space cascade,前提是该日志所在表空间启用了自动段空间管理(assm),且已启用行移动。
常见错误现象:ORA-10636: row movement is not enabled 或 ORA-10637: segment shrink is not supported for this object type —— 后者通常出现在对物化视图主表(即 ON PREBUILT TABLE 方式创建的 MV)直接 shrink 时,因为这类 MV 本质是普通表,但 Oracle 不允许对其 shrink,除非它被显式定义为可移动段且无依赖约束/触发器干扰。
- 确认物化视图是否基于预建表:
SELECT on_prebuilt_table FROM dba_mviews WHERE mview_name = 'YOUR_MV' - 若为预建表,先检查其底层表是否满足 shrink 条件:
SELECT segment_type, tablespace_name, segment_space_management FROM dba_tablespaces t, dba_segments s WHERE s.tablespace_name = t.tablespace_name AND s.segment_name = 'YOUR_BASE_TABLE' AND s.owner = 'OWNER' - 物化视图日志名一般形如
MLOG$_BASETABLE,它是普通表,可 shrink;执行前务必ALTER TABLE MLOG$_XXX ENABLE ROW MOVEMENT
分区表收缩要分步处理,不能一招通吃
对分区表执行 shrink space 默认只作用于整个段(即所有分区),但实际中更常见的是某几个老分区数据被大量删除、HWM 滞高、空块堆积。这时直接 shrink 整个表会锁表时间长、I/O 压力大,还可能失败。
推荐做法是按分区 shrink,语法为:ALTER TABLE t_name MODIFY PARTITION p_name SHRINK SPACE CASCADE。注意:此操作要求分区本身是独立段(非 interval 分区的自动创建分区,且未被标记为 READ ONLY)。
- 先查出哪些分区空块多:
SELECT partition_name, blocks, empty_blocks, num_rows FROM dba_tab_partitions WHERE table_owner='OWNER' AND table_name='T_NAME' ORDER BY empty_blocks DESC - 对目标分区 shrink 前,确保该分区上无 DML 正在运行;否则会报
ORA-10631: SHRINK clause should not be specified for tables with enabled constraints—— 实际是因约束校验阻塞,不是约束本身问题,暂停应用写入更稳妥 - shrink 分区后索引会自动维护(
CASCADE保证),但全局索引需额外UPDATE INDEXES,否则会失效
deallocate unused 和 shrink space 的关键区别在哪?
deallocate unused 只释放高水位线(HWM)之上的未用空间,不移动数据、不降低 HWM;而 shrink space 会把数据往段前端压缩、下移 HWM、并真正释放空间回表空间。前者快、低风险、无需行移动;后者彻底、但耗资源、需锁表、有兼容性限制。
典型误用场景:刚 delete 掉 90% 数据,立刻跑 deallocate unused,发现表大小没变 —— 因为 HWM 没动,空块全在 HWM 下方,deallocate 对它无效。
- 判断是否该用 shrink:
SELECT blocks, empty_blocks, num_rows FROM dba_tables WHERE table_name='T_NAME';若empty_blocks很大但num_rows很小,说明 HWM 滞高,必须 shrink -
deallocate unused keep 1M中的keep是保留至少 1MB 在 HWM 上方,防止后续 insert 频繁扩展 extent,但和 HWM 无关 - 分区表上执行
deallocate unused仅作用于当前分区(需加FOR PARTITION p_name),不跨分区
物化视图刷新后空间暴增,怎么快速回收?
物化视图完全刷新(COMPLETE)默认走 TRUNCATE + INSERT /*+ APPEND */,这会导致新数据写在段尾、HWM 猛涨,旧空间未重用。即使删了原数据,表大小也不缩。此时最有效的是在刷新后立即对物化视图基表(或预建表)执行 shrink。
但要注意:如果物化视图正在被查询(尤其有 fast refresh 依赖),shrink 可能被阻塞,报 ORA-00054: resource busy。这不是锁表失败,而是 Oracle 内部结构校验等待。
- 刷新完成后,立刻检查基表 HWM:
ANALYZE TABLE mv_base_table COMPUTE STATISTICS(仅用于 HWM 获取,11g+ 也可用DBMS_SPACE.SPACE_USAGE) - 若确认
empty_blocks > 0,再执行:ALTER TABLE mv_base_table ENABLE ROW MOVEMENT; ALTER TABLE mv_base_table SHRINK SPACE CASCADE; - 别忘了重建物化视图日志上的索引(如
I_MLOG$_XXX),它们常因日志膨胀而碎片严重,且 shrink 日志表不会自动 rebuild 其索引
真正难的不是命令怎么写,而是判断「该不该 shrink」——HWM 是否真高、空块是否真实闲置、下游应用是否容忍短时锁表。很多 DBA 直接套用脚本批量 shrink,结果在业务高峰卡住核心表,比空间不足还致命。

















