on commit刷新使update变慢数十倍,因其强制在提交前同步写入mlog$_xxx日志并执行merge更新mv,且日志缺索引、字段缺失或未启用并行时会退化为串行全量刷新。

物化视图日志(MLOG$_xxx)本身不直接“变慢”基表写入,但它是基表 INSERT/UPDATE/DELETE 变慢的**关键触发器和放大器**——尤其当物化视图设为 ON COMMIT 时,每次 DML 都会同步触发对日志表的写入+后续刷新逻辑,形成链式开销。
为什么 ON COMMIT 刷新会让 UPDATE 慢几十倍?
Oracle 在基表执行 UPDATE 时,若存在 ON COMMIT 物化视图,事务提交前必须完成两件事:一是把变更记录写入 MLOG$_xxx 日志表;二是调用 MERGE INTO MV_NAME 同步更新物化视图。这两步都串行在提交路径上。
- 日志写入不是简单 INSERT:它要维护
XID$$、DMLTYPE$$、OLD_NEW$$、CHANGE_VECTOR$$等隐藏字段,且需保证日志与基表事务原子性 -
MERGE执行本身可能走全表扫描(比如日志积压、无索引、基表没ROWID或主键),trace 文件里常看到MERGE INTO "SCOTT"."MV_EMP"占用 90%+ 耗时 - 如果物化视图定义含聚合或连接,
ON COMMIT会静默退化为COMPLETE刷新,等价于每次提交都重建整个 MV
MLOG$_xxx 表没索引,DML 就会越跑越慢
物化视图日志表默认不带任何索引,而 Oracle 刷新时依赖 SELECT ... FROM MLOG$_xxx WHERE XID$$ = :1 拉取本次事务变更。没有索引,就是全表扫描日志表——随着日志数据堆积(尤其未及时 purge),每次 DML 提交前的查询越来越慢。
- 必须手动建索引:
CREATE INDEX I_MLOG$_EMP ON MLOG$_EMP(XID$$) NOLOGGING PARALLEL 4 - 如果日志含
SEQUENCE$$(用于 FAST 刷新),也建议加复合索引:CREATE INDEX I_MLOG$_EMP_XID_SEQ ON MLOG$_EMP(XID$$, SEQUENCE$$) - 别依赖
AUTOTRACE或执行计划看日志访问:真实刷新走的是内部递归 SQL,得查 AWR 中 sql_id 包含MVIEW$_或MLOG$的语句
日志结构缺失关键字段,强制退化为串行 COMPLETE 刷新
哪怕只缺一个字段,Oracle 就无法做 FAST 刷新,ON COMMIT 会退化成锁表级的 COMPLETE 刷新——此时基表 DML 不是“慢”,而是被阻塞到刷新结束。
- 必须包含
ROWID:CREATE MATERIALIZED VIEW LOG ON emp WITH ROWID,否则连最基础的 FAST 都不支持 - 涉及
UPDATE或DELETE,必须加INCLUDING NEW VALUES,否则无法捕获新旧值差异 - 多表 JOIN 场景下,每个基表日志都得有
ROWID+SEQUENCE$$+ 所有 JOIN 列(如deptno),漏一个就退化 - 检查方式:
SELECT * FROM USER_MVIEW_LOGS WHERE MASTER = 'EMP',确认ROWIDS、PRIMARY_KEY、SEQLIST列值非空
真正卡点不在日志表本身,而在刷新参数与会话配置错配
很多人以为建了日志就万事大吉,结果 ON COMMIT 还是慢——根本原因是刷新引擎根本没启用并行,所有日志处理挤在一个会话里串行执行。
-
DBMS_MVIEW.REFRESH内部调用受会话控制:必须提前执行ALTER SESSION ENABLE PARALLEL DML,否则即使日志再规范,也只会单线程处理 -
atomic_refresh => FALSE必须配合使用:否则TRUNCATE + INSERT会锁死物化视图段,基表 DML 等待library cache lock - 不要设
parallelism > 4:超过 CPU_COUNT × 2 容易引发enq: PS - contention,反而拖慢整体响应 - 最关键的验证动作:刷新期间查
V$PX_SESSION,若只有 1 个 Q000 进程,说明并行完全没生效
日志表本身只是个容器,真正让写入变慢的是它撬动的整条链路:从基表 DML 触发日志写入,到事务提交前强制刷新,再到刷新时因配置缺失被迫串行扫描、锁表、重算。每一环都可优化,但最容易被忽略的是——你根本没确认 EXPLAIN_MVIEW 输出里那句 "POTENTIAL FAST REFRESH" 是不是真被 Oracle 采纳了。











