必须运行DBMS_MVIEW.EXPLAIN_MVIEW('MV_NAME')查PLAN_TABLE中REFRESH_FAST的POSSIBLE与MSGTXT,结合MVIEW_EXCEPTIONS的RECOMMENDATION定位根因,再检查日志表HWM、共享积压及atomic_refresh与parallelism配对设置。

直接跑 DBMS_MVIEW.EXPLAIN_MVIEW,别猜日志或执行计划
物化视图刷新变慢,90% 以上不是 SQL 写得差,而是 Oracle 悄悄把本该 FAST 的刷新降级成了 COMPLETE,且不报错、不提示。唯一能确认它“为什么不能快刷”的入口,就是 DBMS_MVIEW.EXPLAIN_MVIEW。它不依赖你对日志结构的猜测,也不看你手写的 SQL 是否“看起来合理”,而是调用内核判断逻辑,把每条限制规则的满足情况摊开给你看。
必须这样执行:
- 先运行
EXEC DBMS_MVIEW.EXPLAIN_MVIEW('MV_NAME');(注意单引号) - 结果默认写入
PLAN_TABLE(不是临时表,也不是内存),查时用:SELECT CAPABILITY_NAME, POSSIBLE, MSGTXT, RELATED_TEXT FROM PLAN_TABLE WHERE CAPABILITY_NAME = 'REFRESH_FAST';
- 重点关注
POSSIBLE = 'N'且MSGTXT非空的行——那才是真实卡点
MSGTXT 里出现这些内容,就锁定了根因
MSGTXT 是 Oracle 给你的诊断人话,比 ORA 错误码直接十倍。常见几类提示对应明确动作:
-
"REFRESH FAST IS NOT POSSIBLE"→ 不是配置漏了,是定义或基表状态硬性不满足,必须逐条查RECOMMENDATION字段(需从MVIEW_EXCEPTIONS表查) -
"NO LOG ON BASE TABLE"→ 至少一张基表没建日志,比如MLOG$_ORDERS根本不存在 -
"missing rowid"或"sequence column not logged"→ 日志建了但缺ROWID或SEQUENCE关键字段 -
"join not supported"→ 查询含LEFT JOIN或UNION ALL,Oracle 内核级禁用 FAST -
"expression not allowed in select list"→ 用了UPPER(name)、SYSDATE等非确定性表达式
查完 EXPLAIN_MVIEW 还慢?盯住日志表高水位和共享日志积压
即使 EXPLAIN_MVIEW 显示 POSSIBLE = 'Y',实际刷新仍慢,问题一定出在日志表本身:
- 查空间是否虚高:
SELECT bytes/1024/1024 AS mb FROM dba_segments WHERE segment_name = 'MLOG$_YOUR_TABLE';—— 若远大于SELECT COUNT(*) FROM MLOG$_YOUR_TABLE;,说明高水位(HWM)悬空,Oracle 仍在全扫空块 - 收缩 HWM:
ALTER TABLE MLOG$_YOUR_TABLE ENABLE ROW MOVEMENT;,再ALTER TABLE MLOG$_YOUR_TABLE SHRINK SPACE COMPACT; - 多个 MV 共享同一日志时,只要有一个 MV 刷得慢或长期未刷,日志就持续堆积。查共享关系:
SELECT mview_name, last_refresh_date FROM user_mviews WHERE mview_name IN (SELECT mview_name FROM user_snapshot_logs WHERE master = 'YOUR_TABLE');
别被 ATOMIC_REFRESH 和 PARALLELISM 参数骗了
设了 parallelism => 4 却还是单线程,大概率是 atomic_refresh 还在默认 TRUE。这个参数不关,所有并行进程都挤在一个事务里等提交,锁、undo、TX 等待全来了。
- 必须配对使用:
DBMS_MVIEW.REFRESH('mv_name', method => 'F', parallelism => 4, atomic_refresh => FALSE) -
ATOMIC_REFRESH => FALSE后,刷新变成分批提交(如每 5 万行 commit 一次),锁粒度变小,但 MV 在刷新中可能短暂返回新旧混合数据 - 切忌同时设
refresh_after_errors => TRUE:出错跳过批次后,已提交部分无法回滚,状态不可逆 - 验证是否真并行:
SELECT * FROM v$PX_SESSION,刷新期间应看到多个 PX 进程;若只看到 1 个,说明parallelism被忽略或降级
真正容易被忽略的是:EXPLAIN_MVIEW 输出里的 RECOMMENDATION 字段,它会精准指出语法级限制点,比如哪一行 SQL 用了不支持的子查询、哪个表达式触发了非确定性判定。不查它,光看 MSGTXT 只能知道“不行”,查了才知道“具体哪一列、哪一函数、哪一连接方式”坏了事。


















