Oracle物化视图增量刷新依赖MLOG$日志表、基表主键/唯一约束及SNAPSHOT LOG内容;分区变更后需验证PCT支持、日志覆盖范围与统计信息更新,否则易退化为完全刷新。
物化视图增量刷新依赖什么机制
oracle 物化视图的增量刷新(fast refresh)不是靠“自动识别数据变化”,而是严格依赖 mlog$ 日志表 + 基表上的主键或唯一约束 + 刷新时的 snapshot log 内容。对大表做分区变更(比如 add partition、split partition)后,如果基表有物化视图依赖,必须确认日志是否覆盖新增分区范围。
常见错误现象:ORA-12052: cannot fast refresh materialized view 或刷新后数据重复/丢失。根本原因常是:新增分区未被 DBMS_MVIEW.EXPLAIN_MVIEW 识别为可增量范围,或日志表没在新分区上记录 DML。
- 分区表必须提前建好
SNAPSHOT LOG,且需带WITH ROWID和INCLUDING NEW VALUES(尤其涉及INSERT场景) - 执行
ALTER TABLE ... ADD PARTITION后,日志表不会自动扩展到新区;但只要原日志支持全表 ROWID(即非ON COMMIT模式下用ROWID而非PRIMARY KEY),通常仍可刷新 - 若使用
PRIMARY KEY模式日志,新增分区中若含新主键值,且该值未被日志捕获(如分区刚加完就直接 DML),则下次 FAST REFRESH 会退化为 COMPLETE
分区变更后如何验证能否继续增量刷新
别猜,直接查 Oracle 的内部判断逻辑。核心是调用 DBMS_MVIEW.EXPLAIN_MVIEW 并检查 MSGTXT 字段是否含 "fast" and "refresh" 相关肯定描述。
BEGIN
DBMS_MVIEW.EXPLAIN_MVIEW('MV_SALES_DAILY');
END;
/
<p>SELECT capability_name, possible, related_text, msgtxt
FROM mv_capabilities_table
WHERE mvname = 'MV_SALES_DAILY'
AND capability_name IN ('REFRESH_FAST', 'REWRITE_FULL', 'REWRITE_PARTIAL')
ORDER BY seq;</p>
关键看:
-
possible = 'Y'且msgtxt不含"cannot fast refresh"才算真正支持 - 如果出现
"partition change tracking not supported",说明当前物化视图定义未启用 PCT(Partition Change Tracking),需重建并显式加ENABLE QUERY REWRITE和PCT属性 - 注意:PCT 仅在基表和物化视图都按相同列分区、且物化视图含
GROUP BY或聚合时才生效;纯 SELECT * 类型 MV 不走 PCT
大表分区变更后强制启用 PCT 增量刷新的实操条件
PCT 不是开关,是一组强约束组合。要让 Oracle 在分区 SPLIT/EXCHANGE 后仍走增量,必须同时满足:
- 基表启用
ROW MOVEMENT(否则MOVE PARTITION会失败,间接影响日志一致性) - 物化视图创建时指定
ENABLE QUERY REWRITE且含PCT关键字(不是默认行为) - 物化视图定义中的
SELECT必须包含分区键列(哪怕只是SELECT t.*, t.part_key FROM ...),否则优化器无法映射分区变更 - 基表的
SNAPSHOT LOG必须建在分区键所在列上,且不能是函数索引列(如TO_DATE(part_col))
示例关键片段:
CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID, SEQUENCE (sale_id, region, sale_date) INCLUDING NEW VALUES; <p>CREATE MATERIALIZED VIEW mv_sales_region BUILD IMMEDIATE REFRESH FAST ON DEMAND ENABLE QUERY REWRITE PCT -- ← 这个关键字不能少 AS SELECT region, COUNT(*) cnt, SUM(amount) amt FROM sales GROUP BY region;</p>
为什么分区交换(EXCHANGE PARTITION)后物化视图常变慢甚至卡住
EXCHANGE PARTITION 看似原子操作,但物化视图日志不感知“数据搬移”——它只记录 DML。交换进来的新分区若没经过 DML,其数据对日志而言就是“不可见”的。下次 FAST REFRESH 会尝试扫描整个基表找变更,退化为伪全量。
更隐蔽的问题是统计信息:交换后基表全局统计信息未更新,CBO 可能误判物化视图重写路径,导致查询走错执行计划,连带刷新也选错驱动表。
- 交换后立即执行:
DBMS_STATS.GATHER_TABLE_STATS,目标是基表本身,不是日志表 - 避免在交换窗口期内触发刷新;建议交换完成后再手工跑一次
DBMS_MVIEW.REFRESH并设method => 'F'(强制 FAST),观察是否报错 - 如果已发生退化,不要反复重试;先查
mv_capabilities_table,再考虑临时切到COMPLETE刷新一次,重建日志一致性
分区变更和物化视图的交集处,最易被忽略的是日志表的物理分布——它默认不继承基表分区结构,大表日志容易成为单点热点,而这个问题在 EXPLAIN_MVIEW 输出里完全不体现。











