千万级分区表物化视图FAST刷新卡在MLOG$扫描,因DBMS_MVIEW.REFRESH默认全表扫描+排序去重,日志无索引、统计过期或SNAPTIME$$不准会引发磁盘排序,响应骤升;需建复合索引、更新统计、检查并清理截断日志。

为什么千万级分区表的物化视图刷新总卡在 MLOG$ 扫描?
因为 DBMS_MVIEW.REFRESH 默认对物化视图日志(MLOG$_YOUR_TABLE)做全表扫描 + 排序去重,而千万级分区表一次批量更新可能产生百万级日志条目。若日志表没索引、统计信息过期或 SNAPTIME$$ 精度不准,就会触发磁盘排序,响应时间从秒级飙升到分钟级甚至超时。
常见错误现象:V$SESSION_WAIT 显示大量 direct path read temp 或 enq: TS - contention;执行计划里出现 TABLE ACCESS FULL on MLOG$_*。
- 必须建复合索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$) - 立即更新统计信息:
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'MLOG$_SALES') - 检查
SNAPTIME$$是否被截断:查SELECT COUNT(*) FROM MLOG$_SALES WHERE SNAPTIME$$ = TO_DATE('4000-01-01', 'YYYY-MM-DD'),非零说明日志未被消费,需人工干预
如何让 FAST 刷新真正按分区粒度运行?
Oracle 11g 原生不支持 REFRESH FOR PARTITION,所谓“局部刷新”本质是靠 ROWID 范围 + SNAPTIME$$ 时间戳联合过滤日志。缺一不可的三个硬性前提必须全部满足,否则静默降级为 COMPLETE:
- 基表是单列
RANGE或LIST分区(如sale_date),不能是HASH或复合分区键 - 物化视图定义中
SELECT和GROUP BY都显式包含该分区键(不能用别名,不能写TRUNC(sale_date)却只在日志里记sale_date) - 建 MV 时必须带
ENABLE QUERY REWRITE和PCT(不是可选项)
验证是否生效:DBMS_MVIEW.EXPLAIN_MVIEW('MV_SALES') 后查 mv_capabilities_table,确认 REFRESH_FAST 的 possible 为 Y,且 msgtxt 不含 "partition change tracking not supported"。
ATOMIC_REFRESH=FALSE 和 PARALLELISM 怎么配才安全又快?
ATOMIC_REFRESH => FALSE 是提速关键,但它不是“更安全的增量”,而是“更快的破坏性覆盖”:直接 TRUNCATE + INSERT /*+ APPEND */,跳过临时表和事务包装。必须配合 PARALLELISM 才能真正释放并行能力——否则即使设了 parallelism => 4,仍会因事务锁阻塞在 enq: TX - row lock contention。
- 推荐组合:
atomic_refresh => FALSE+parallelism => 2或4(不要超过CPU_COUNT * 2) - 风险点很具体:刷新失败时,物化视图变空表,下游查询立刻报
ORA-01403: no data found - 绝对不能用于
ON COMMIT场景(违反原子性语义) - 不要同时设
refresh_after_errors => TRUE:出错后已提交的部分无法回滚,状态不可逆
如何避免存储过程里调用刷新变成定时雷?
在业务存储过程中硬编码 DBMS_MVIEW.REFRESH(..., 'F') 是高危操作。FAST 刷新依赖日志连续性、查询结构稳定性及 SNAPTIME$$ 对齐,而存储过程执行时完全无法感知这些上下文。
- 前置检查必须做:
SELECT STALENESS, LAST_REFRESH_DATE FROM USER_MVIEWS WHERE MVIEW_NAME = 'MV_SALES',若STALENESS = 'UNUSABLE',说明上次刷新已损坏,此时'F'必然失败 - 别用
'?'(即FORCE)当兜底:它会静默降级为COMPLETE,你只能事后翻V$SESSION_LONGOPS或对比LAST_REFRESH_DATE偏移才能发现 - 捕获异常要具体:
WHEN OTHERS THEN IF SQLCODE = -12004 THEN ...,而不是只写EXCEPTION WHEN OTHERS
真正容易被忽略的是 SNAPTIME$$ 的精度问题——它不是系统时钟,而是每个物化视图自己的水位线。如果上次刷新时间戳是 2026-08-25 14:30:00,新刷就必须从这个时间之后取日志,差一秒都可能漏数据或重复应用。


















