ORA-01555 根本原因是刷新事务过长导致UNDO前镜像被覆盖,而非磁盘空间不足;应优先改atomic_refresh=>FALSE或分批刷新,而非扩容UNDO表空间。
物化视图刷新卡住并报 ORA-01555,本质是快照过旧不是磁盘空间问题
根本原因不是回滚段(或 undo 表空间)物理空间不足,而是刷新事务运行太久,undo 中的前镜像被覆盖,导致查询一致性读失败。oracle 需要基于 scn 构建“读一致性视图”,而长时间运行的刷新会持续拉长这个需求窗口。
-
ORA-01555出现时,V$UNDOSTAT里常能看到UNXPSTEALCNT或SSOLDERRCNT上升,说明 UNDO 被主动回收或复用 - 不要第一反应去扩容
UNDO_TABLESPACE——增大空间只能延缓问题,不能解决长事务本质 - 确认是否用了
ON COMMIT刷新:这类刷新隐式绑定在用户事务中,极易因提交间隔长触发ORA-01555
DBMS_MVIEW.REFRESH 的 atomic_refresh 参数决定是否走 INSERT/DELETE 大扫荡
默认 atomic_refresh => TRUE 会让刷新变成一个完整事务:先 DELETE 全量旧数据,再 INSERT 新数据。这对大物化视图就是灾难——UNDO 消耗陡增,且锁表时间长、易撞 ORA-01555。
- 改为
atomic_refresh => FALSE:Oracle 改用TRUNCATE + INSERT /*+ APPEND */,UNDO 消耗锐减,且不依赖长时间一致性读 - 但注意:这要求物化视图不含
ON COMMIT刷新、无未决主键冲突、且目标表无外键引用(否则TRUNCATE失败) - 执行前加
ALTER SESSION ENABLE PARALLEL DML,配合/*+ PARALLEL(mv_name, 4) */可进一步缩短窗口
大物化视图必须分批刷新,靠 rowid 或分区键切片,别信 job_queue_processes
单纯调高 job_queue_processes 或开并行,无法解决单次刷新事务过长的问题。真正有效的是把“一次全刷”拆成多个短事务。
- 若物化视图基表可按
ROWID切分:用DBMS_ROWID.ROWID_CREATE构造区间,每次刷 50k–200k 行,配合COMMIT - 若基表已分区(如按日期),优先用
DBMS_MVIEW.REFRESH的list参数指定单个分区名,逐个刷新 - 避免用
WHERE子句在刷新 SQL 里过滤——物化视图定义里的查询逻辑不可 runtime 修改,硬加条件会导致刷新结果不一致
UNDO_RETENTION 不是越大越好,要和 undo_management=AUTO 下的实际保留策略对齐
UNDO_RETENTION=3600 只是“建议值”,当 UNDO 表空间不足时,Oracle 仍会覆盖未过期的 UNDO 段。真正起作用的是 RETENTION GUARANTEE 和实际空间压力。
- 查当前有效保留:运行
SELECT MAX(maxquerylen), MAX(tuned_undoretention) FROM V$UNDOSTAT,后者才是 Oracle 动态调整后的实际值 - 开启
RETENTION GUARANTEE(需 DBA 权限):防止 UNDO 被强制覆盖,但代价是可能引发ORA-30036(无法扩展 undo 表空间) - 更稳妥的做法:监控
V$UNDOSTAT.UNEXPIREDBLK占比,长期 >80% 就说明UNDO_TABLESPACE确实小了,此时扩容才有意义
复杂点在于,物化视图刷新路径混杂了隐式事务、并行机制和底层段操作,很多 DBA 习惯性调参数却漏掉最该动的地方——把原子刷新改成非原子,或者把全量改成按分区/ROWID 分批。这两个动作改完,ORA-01555 基本就不再随随便便冒出来。










