根本原因是快速刷新需全量扫描MLOG$日志表,无索引+统计信息过期导致全表扫描与磁盘排序;应建(snaptime$$,sequence$$)复合索引、gather_table_stats全采样、定期purge日志,并在大分区刷新时启用atomic_refresh=FALSE以truncate+append提速。

为什么分区表上的物化视图刷新总卡在MLOG$扫描?
因为Oracle快速刷新依赖物化视图日志(MLOG$),而分区表大批量更新后,日志条目常达百万级。DBMS_MVIEW.REFRESH默认全量扫描日志并排序去重——若没建复合索引、统计信息过期或日志堆积,就会触发磁盘排序和全表扫描,响应时间飙升。
- 查日志表是否已有索引:
SELECT index_name FROM user_indexes WHERE table_name = 'MLOG$_YOUR_TABLE' - 必须建的复合索引:
CREATE INDEX idx_mlog_seq ON MLOG$_YOUR_TABLE (snaptime$$, sequence$$) - 立刻更新统计信息:
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'MLOG$_YOUR_TABLE') - 清理陈旧日志(保留1天):
EXEC DBMS_MVIEW.PURGE_LOG('YOUR_MASTER_TABLE', 1)
如何让刷新只扫某个分区,而不是整张日志表?
Oracle原生不支持REFRESH ... FOR PARTITION语法,但可通过ROWID + SNAPTIME$$间接控制扫描范围。前提是日志建时含WITH ROWID, SEQUENCE,且物化视图定义不含聚合、连接等阻断分区感知的结构。
- 先查目标分区名:
SELECT partition_name FROM user_tab_partitions WHERE table_name = 'YOUR_TABLE' AND high_value LIKE '%2026-07%' - 构造谓词限制日志扫描:
WHERE mview_log_rowid IN (SELECT ROWID FROM YOUR_TABLE PARTITION (P202607)) - 调用刷新时设
atomic_refresh => FALSE,避免临时表开销;但注意失败即清空MV - 关键点:
SNAPTIME$$精度必须对齐——如果上次刷新是2026-07-27 23:59:59,新刷就得从这个时间戳之后取日志
PCT(Partition Change Tracking)到底要不要开?
要开,但仅当满足全部硬性条件:基表是RANGE/LIST分区、分区键为单列、物化视图SELECT和GROUP BY中都显式包含该分区键。否则Oracle会静默降级为COMPLETE刷新,且DBA_MVIEWS.CAN_USE_LOG = 'NO'。
- 验证是否真支持PCT:
BEGIN DBMS_MVIEW.EXPLAIN_MVIEW('MV_NAME'); END;,再查mv_capabilities_table里REFRESH_FAST的possible是否为Y - PCT在10gR2+无需物化视图日志,但要求基表启用
ROW MOVEMENT(否则EXCHANGE PARTITION会失败) - 纯
SELECT *类MV不走PCT;带GROUP BY且跨多天统计的MV,若分区键不在GROUP BY中,PCT直接失效
ATOMIC_REFRESH=FALSE在分区刷新中怎么用才安全?
它跳过事务包装和临时表,直接TRUNCATE + INSERT /*+ APPEND */,对大分区能提速3–5倍,但失败时MV变空表,下游查询报ORA-01403: no data found。
- 仅在确认分区数据完整、且下游可容忍短暂空窗时启用
- 必须配合
refresh_after_errors => FALSE,否则一个分区失败就中断整个list刷新 - 别忽略
rollback_seg参数——大分区插入可能撑爆小回滚段,建议显式指定大回滚段或使用UNDO表空间 - 最易被忽略的一点:
ATOMIC_REFRESH=FALSE下,SNAPTIME$$不会自动更新,需手动同步:UPDATE MLOG$_T SET SNAPTIME$$ = SYSDATE WHERE SNAPTIME$$ = (SELECT LAST_REFRESH_DATE FROM DBA_MVIEWS WHERE MVIEW_NAME = 'MV_NAME')


















