根本原因是FAST刷新退化为COMPLETE模式且atomic_refresh=TRUE,导致全表扫描;需检查物化视图日志状态、设atomic_refresh=FALSE、关闭并行。
物化视图刷新触发基表全表扫描,根本原因不是“它想扫”,而是 fast 刷新路径被迫退化为 complete,且未加任何谓词过滤——dbms_mview.refresh 会直接执行物化视图定义查询(select ... from base_table),而该查询若无有效索引或统计信息偏差,优化器就选了 fts。
FAST 刷新失效时自动降级为 COMPLETE 扫描
当物化视图日志(MLOG$_xxx)损坏、缺失、SEQUENCE 字段为 'NO',或基表刚执行过 EXCHANGE PARTITION 等 DDL 操作,DBMS_MVIEW.REFRESH 会静默放弃 FAST,转而走 COMPLETE 模式。此时 Oracle 不再读日志增量,而是重新执行物化视图的原始 SELECT 语句——如果这个语句本身没 WHERE 条件、没索引支撑、或统计信息过期,优化器大概率生成 TABLE ACCESS FULL 执行计划。
- 查日志状态:
SELECT LOG_TABLE, ROWIDS, SEQUENCE FROM USER_MVIEW_LOGS WHERE MASTER = 'YOUR_TABLE';若SEQUENCE = 'NO',FAST 已不可用 - 查当前刷新方式:
SELECT FAST_REFRESHABLE FROM USER_MVIEWS WHERE MVIEW_NAME = 'MV_NAME';返回值不是'FAST'就别指望增量 - COMPLETE 模式下不看日志,只跑原 SQL——哪怕你只改了一行,它也扫全表
atomic_refresh => TRUE 强制事务一致性,抑制谓词下推
默认 atomic_refresh => TRUE 时,刷新被包在一个事务里:先 DELETE 旧数据,再 INSERT /*+ APPEND */ 新数据。为保证可重复读(consistent read),Oracle 必须对基表所有参与刷新的行构造前镜像(undo),这就要求它提前锁定/扫描全部候选行——即使原查询有 WHERE 条件,也可能因事务隔离级别限制,无法下推谓词到基表扫描层,最终退化为全表扫描后再过滤。
- 典型现象:
v$session.event显示大量db file sequential read或enq: TX,同时执行计划出现 FTS - 解决方向:设
atomic_refresh => FALSE后,刷新变为TRUNCATE + INSERT /*+ APPEND */,不再需要 undo 前镜像,谓词能正常下推,索引才可能被用上 - 但注意:
atomic_refresh => FALSE在备库(OPEN_MODE = 'READ ONLY')会报ORA-16000,必须在主库执行
并行刷新(PARALLELISM > 0)加剧扫描范围与锁争用
开并行本意是提速,但在 11g 中常适得其反:多个并行进程并发扫描基表,不仅放大 I/O 压力,还导致优化器更倾向选择 FTS(因并行全表扫描的 direct path read 成本模型更低)。尤其当基表缺少 (snaptime$$, sequence$$) 复合索引时,并行读 MLOG$_xxx 会引发大量临时段排序,拖慢整个刷新批次,间接延长基表扫描窗口。
- 检查是否真需并行:
SELECT PARALLELISM FROM USER_MVIEWS WHERE MVIEW_NAME = 'MV_NAME' - OLTP 场景建议设
PARALLELISM => 0;若必须并行,先建索引:CREATE INDEX idx_mlog_snap_seq ON MLOG$_XXX (snaptime$$, sequence$$) - 并行扫描触发的
direct path read会绕过 buffer cache,物理读量翻倍,AWR 中表现为高direct path read等待
真正可控的点其实就三个:确认日志可用性、强制 atomic_refresh => FALSE、关闭无谓并行。其他如统计信息、索引设计、查询写法,都是“基表自己该管的事”——物化视图刷新不会替你优化它。


















