会,ON COMMIT刷新会卡住事务,因每次COMMIT前必须完成MV刷新,导致写入延迟激增、锁争用加剧,仅适用于写入极低且实时性要求苛刻的场景。
ON COMMIT 刷新会卡住你的事务吗?
会,而且卡得挺实在。只要物化视图定义里写了 refresh on commit,每次执行 commit 时,数据库必须先完成刷新(包括查询基表、计算差异、更新 mv 日志、写入 mv 表),才能真正提交事务。这意味着:事务的响应时间直接受 mv 刷新耗时支配。
常见错误现象:ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view(权限或 MV 日志缺失)、ORA-12008: error in materialized view refresh path(基表变更触发刷新失败,导致整个事务回滚)。
- 必须确保基表已建好
MATERIALIZED VIEW LOG,且包含所有 SELECT 列和WITH ROWID或WITH PRIMARY KEY -
ON COMMIT不支持含聚合、分析函数、远程表、非确定性函数(如SYS_GUID())的 MV 定义 - 多个
ON COMMITMV 同时依赖同一张基表时,一个刷新失败会导致所有相关事务失败
为什么不用 ON COMMIT 却总被推荐?
因为它语义最干净:数据一致性由数据库内核保证,应用层完全不用操心“什么时候同步”。但代价是把延迟从查询侧(REFRESH ON DEMAND)转移到了写入侧。
使用场景非常窄:仅适用于基表写入频率极低(比如每小时才几笔)、且下游查询对实时性要求苛刻到“不能容忍毫秒级不一致”的系统(例如某些风控规则引擎)。大多数 OLTP 场景下,它反而成了性能瓶颈放大器。
- 写入吞吐量下降明显:单次
INSERT+COMMIT可能从 2ms 拉长到 200ms+ - 并发写入时容易出现锁争用——MV 刷新过程会对基表 MV 日志加行锁,甚至可能锁住 MV 表本身
- Oracle 12c+ 虽支持并行刷新,但
ON COMMIT强制串行执行,无法受益
怎么测出真实延迟和开销?
别信文档里的“平均值”,直接在生产镜像环境跑压测。核心是分离测量:只测刷新本身耗时,排除网络、客户端、应用逻辑干扰。
实操建议:
- 用
DBMS_MVIEW.REFRESH手动触发一次ON COMMIT类型的 MV 刷新,配合V$SESSION_EVENT查看等待事件(重点关注enq: MV - contention和log file sync) - 在事务中插入一条记录后立即
COMMIT,用DBMS_UTILITY.GET_TIME套住整个块,反复执行 100 次取 P95 延迟 - 对比关闭 MV 后的同操作耗时,差值就是纯刷新开销;再对比
REFRESH ON DEMAND的手动刷新耗时,能看出“延迟转移”是否可接受
示例(测单次开销):
DECLARE
t1 NUMBER; t2 NUMBER;
BEGIN
t1 := DBMS_UTILITY.GET_TIME;
INSERT INTO orders VALUES (1001, 'A', SYSDATE);
COMMIT; -- 此处触发 ON COMMIT 刷新
t2 := DBMS_UTILITY.GET_TIME;
DBMS_OUTPUT.PUT_LINE('Commit + refresh took: ' || (t2-t1)/100 || 's');
END;替代方案比硬扛 ON COMMIT 更常用
绝大多数业务其实不需要“提交即可见”,只需要“秒级最终一致”。这时候 REFRESH FAST ON DEMAND 配合定时任务(如 DBMS_SCHEDULER 每 5 秒跑一次)更可控、更可扩展。
关键差异点:
-
ON COMMIT是强同步、高延迟、难诊断;ON DEMAND是弱同步、低写入开销、可监控可重试 - 如果 MV 依赖多张表,
ON COMMIT要求所有基表都带日志且兼容;FAST刷新在部分表无日志时会自动退化为COMPLETE,而ON COMMIT直接报错拒绝创建 - Oracle 19c+ 支持
REFRESH EVERY n SECONDS(基于调度器的隐式周期刷新),语法简洁,但底层仍是ON DEMAND机制
真正复杂的地方在于:MV 日志的维护成本常被低估——它本身要写 redo、占存储、影响基表 DML 性能。哪怕不用 ON COMMIT,只要开了日志,就得持续评估它对主业务的影响。











