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