物化视图complete刷新卡在ora-30036/ora-01555,根本原因是atomic_refresh=true导致undo耗尽;必须设为false启用truncate+append模式,并配合alter session enable parallel dml才能真正提速。

直接上结论:物化视图 COMPLETE 刷新卡在 ORA-30036 或 ORA-01555,根本不是“数据太大”,而是默认事务模式下 UNDO 被撑爆——必须关掉 atomic_refresh => TRUE,改用 TRUNCATE + APPEND 模式,并确保会话级并行可用。
为什么 COMPLETE 刷新会耗尽 UNDO?
Oracle 默认以事务方式执行 DBMS_MVIEW.REFRESH(即 atomic_refresh => TRUE),对大物化视图会先 DELETE 所有旧数据,再逐条 INSERT 新数据。整个过程全程记 UNDO、锁表、阻塞并发写入,且无法中途释放空间。一旦物化视图行数超千万,UNDO 表空间极易被占满,尤其当 UNDO_RETENTION 偏高或存在长事务时。
-
DELETE阶段产生等量 UNDO;INSERT若未走/*+ APPEND */,也持续生成 UNDO - 即使基表已分区、索引已禁用,只要没关
atomic_refresh,就逃不开事务包裹 -
parallelism参数在此模式下完全无效——并行进程全挤在一个事务里排队提交
必须显式设置 atomic_refresh => FALSE
这是解决 UNDO 耗尽最直接有效的动作。设为 FALSE 后,刷新逻辑变为:TRUNCATE 物化视图 → 并行 INSERT /*+ APPEND */ → 提交。全程不记行级 UNDO(只记段级 DDL 和 direct-path 的少量 redo),UNDO 消耗下降 90% 以上。
- 手动刷新:调用时必须写全参数,
DBMS_MVIEW.REFRESH('MV_NAME', atomic_refresh => FALSE) - 定时作业(
DBMS_SCHEDULER):封装的存储过程里不能省略该参数,不能依赖默认值 - 副作用真实存在:物化视图在
TRUNCATE后到INSERT完成前为空,应用查询可能返回空结果集——需业务层容忍或加双读兜底 - 别误以为“设了就万事大吉”:如果物化视图定义含聚合、分析函数,或基表无主键/ROWID,Oracle 会静默退化为更重的伪 COMPLETE 刷新,仍走 DELETE/INSERT
配合启用 PARALLEL DML 才能真正提速
atomic_refresh => FALSE 是前提,但并行插入不会自动生效——必须在同一会话中提前执行 ALTER SESSION ENABLE PARALLEL DML。否则仍是串行 INSERT /*+ APPEND */,只是 UNDO 少了,速度没变。
- 顺序不能错:先
ALTER SESSION ENABLE PARALLEL DML,再调用DBMS_MVIEW.REFRESH - 验证是否真并行:刷新中查
V$PX_SESSION,应看到多个Q00x进程;若只有 1 个,说明开关没起作用 -
parallelism参数仅在此组合下有效,推荐值为2或4;设太高易引发enq: PS - contention - 系统级参数要配平:
parallel_max_servers≥ 并发刷新任务数 × 单次 parallelism,parallel_adaptive_multi_user必须为FALSE
别忽略日志与统计信息的隐性影响
你以为在跑 COMPLETE 刷新,其实底层可能被 Oracle 强制转成带日志扫描的混合模式(尤其当物化视图名匹配某张基表的 MLOG$_* 表时)。这时即使关了 atomic_refresh,仍会去扫日志表,触发全表扫描 + 磁盘排序,间接拉长事务时间、拖累 UNDO 回收。
- 检查是否有残留日志干扰:
SELECT COUNT(*) FROM MLOG$_YOUR_TABLE WHERE snaptime$$ ;积压多就清理:<code>EXEC DBMS_MVIEW.PURGE_LOG('YOUR_TABLE', 1) - 确认日志表有高效索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_YOUR_TABLE (snaptime$$, sequence$$),字段顺序不可颠倒 - 更新统计信息:
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'MLOG$_YOUR_TABLE', estimate_percent => 100),避免优化器误判执行计划
最常被跳过的一步是验证物化视图是否真在走 COMPLETE 模式——用 SELECT refresh_method FROM user_mviews WHERE mview_name = 'MV_NAME' 确认返回 COMPLETE;若返回 FAST,那问题根源其实是日志失效或结构漂移,强行关 atomic_refresh 只是掩盖症状。











