物化视图日志必须包含rowid和sequence才能支持分区感知刷新,否则dbms_mview.refresh无法定位变更分区;oracle快速刷新依赖rowid定位物理位置、sequence保证顺序、snaptime$$做时间区间过滤,三者缺一不可。
物化视图日志必须带 rowid 和 sequence 才能分区感知
没有这两个字段,dbms_mview.refresh 就无法从日志中定位到具体哪几个分区发生了变更。oracle 的快速刷新机制本身不识别分区名,它只认 rowid(指向物理位置)和 sequence$$(保证变更顺序),再结合 snaptime$$ 时间戳做区间过滤。
建日志时漏掉会导致后续所有“按分区刷新”逻辑失效,哪怕你传了 list => 'mv_name',底层还是扫全量日志。
- 正确写法:
CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID, SEQUENCE (sale_date) INCLUDING NEW VALUES; - 错误写法:
CREATE MATERIALIZED VIEW LOG ON sales WITH PRIMARY KEY;—— 这种只能支持简单主键表,不支持分区表增量 - 已有日志不满足?只能先
DROP MATERIALIZED VIEW LOG ON sales;,再重建,不能 ALTER
刷新时传入分区名列表 ≠ 直接指定分区,而是靠 SNAPTIME$$ 对齐
很多人以为 DBMS_MVIEW.REFRESH(list => 'sales_p_202404,sales_p_202405') 是让 Oracle 去查这两个分区,其实不是。Oracle 仍走日志路径,只是你在刷新前手动把这两个分区的 DML 操作对应的时间范围写进 SNAPTIME$$ 字段里(比如设为 TO_DATE('2024-04-01', 'YYYY-MM-DD')),然后调用刷新时它会自动过滤日志中该时间之后的记录。
所以真正起作用的是日志中 SNAPTIME$$ 的精度和一致性,不是 list 参数本身。
- 查当前日志时间戳:
SELECT MIN(snaptime$$), MAX(snaptime$$) FROM mlog$_sales; - 刷新前可手动推进:
EXEC DBMS_MVIEW.REFRESH_DEPENDENT(list => 'sales_mv', method => 'F', atomic_refresh => FALSE);,但前提是日志里已存在对应时间窗口的记录 - 如果分区是按天建的,但
SNAPTIME$$只精确到小时,就会漏掉部分变更——必须确保应用层或 ETL 写入时也控制好事务提交节奏
ATOMIC_REFRESH=FALSE 不是万能提速开关,要配 MERGE 逻辑
设成 FALSE 后,Oracle 不再用 TRUNCATE + INSERT /*+ APPEND */,而是尝试 MERGE,只更新 delta 行。但这个行为能否生效,取决于物化视图定义是否允许 merge:不能含 GROUP BY、JOIN、ROWNUM、DISTINCT 等阻止行级更新的结构。
一旦定义里有聚合,ATOMIC_REFRESH=FALSE 实际上会被忽略,Oracle 自动回退到原子模式,你还以为开了优化。
- 检查 MV 是否支持 merge:
SELECT can_use_log, fast_refreshable FROM user_mviews WHERE mview_name = 'SALES_MV';,若fast_refreshable返回NO或DIRLOADDML,说明不支持真正的增量 merge - 聚合类 MV 必须配合分区键显式重写查询,例如:
WHERE sale_date >= TRUNC(SYSDATE) - 7,再配合日志时间戳控制范围 - 直接路径插入(
INSERT /*+ APPEND */)会跳过触发器,导致日志不记录——这种 DML 无论怎么调参数都刷不到
定期 purge 日志比加索引更关键
日志表 mlog$_sales 膨胀后,即使建了 (snaptime$$, sequence$$) 复合索引,排序去重阶段仍可能因数据量过大引发磁盘排序,V$SESSION_WAIT 显示大量 direct path write temp。
比起反复调优索引,更有效的做法是让日志生命周期可控。
- 清理命令:
EXEC DBMS_MVIEW.PURGE_LOG('sales', 7);—— 保留最近 7 天,注意单位是“天”,不是“条数” - 清理不是删除全部,而是删
snaptime$$ 的记录,不影响正在消费的日志 - 千万别在高峰期手动
DELETE FROM mlog$_sales,没走PURGE_LOG接口会破坏内部状态,下次刷新直接报ORA-12008
最常被忽略的一点:分区表物化视图的“动态分区”本质不是数据库自动识别分区边界,而是你通过日志时间戳、DML 路径控制、MV 定义约束三者协同构造出的逻辑分区感。任何一环松动,整个链路就退回全量扫描。











