是,ON COMMIT刷新会锁主表;因其每次提交均触发日志写入与MV更新,需加行级或表级锁以保证一致性。应改用ON DEMAND刷新,配合atomic_refresh=>FALSE实现只读快照。
物化视图刷新时主表被锁住,是因为用了 ON COMMIT 刷新?
默认 on commit 刷新模式下,每次提交都会触发物化视图日志写入和 mv 更新,主表(基表)需加行级锁甚至表级锁(尤其涉及 fast 刷新的约束检查或日志清理),导致 dml 堵塞。这不是“争用”而是设计行为——oracle 必须保证刷新时刻数据一致性。
解决思路不是调参数,而是换刷新策略:停用 ON COMMIT,改用基于只读快照的 ON DEMAND 刷新,并确保刷新期间主表完全不受影响。
如何创建真正只读的查询快照(不依赖主表锁)?
关键在两步:一是用 FOR UPDATE 以外的方式固化快照点;二是让物化视图刷新走独立事务、不干扰主表 DML。
- 建物化视图时显式指定
BUILD IMMEDIATE REFRESH FAST ON DEMAND,禁用自动提交关联 - 刷新前手动执行
DBMS_MVIEW.REFRESH并传入atomic_refresh => FALSE:这会让 Oracle 先 truncate 再 insert,跳过对主表的锁定校验 - 确保物化视图日志已建且含
ROWID和必要列(INCLUDING NEW VALUES),否则FAST刷新会退化为COMPLETE
示例刷新命令:
EXEC DBMS_MVIEW.REFRESH('MV_SALES_SUM', 'F', atomic_refresh => FALSE);
为什么 atomic_refresh => FALSE 能绕过主表争用?
默认 atomic_refresh => TRUE 时,Oracle 用单事务完成 delete+insert,需在主表上做一致性读(可能触发 SCN 锁等待);设为 FALSE 后,它转为两个独立 DDL 操作:先 TRUNCATE 物化视图表(无锁主表),再用并行 INSERT /*+ APPEND */ 从主表 SELECT —— 此时主表只是被读,不加任何写锁。
注意副作用:
- 刷新窗口内物化视图短暂为空(应用需容忍空结果或加双 MV 切换)
- 不能用于有未提交事务依赖该 MV 的场景(因非原子性)
-
TRUNCATE会重置高水位线,后续首次查询可能慢(可预热)
还有哪些隐藏坑会影响“只读快照”效果?
即使用了 atomic_refresh => FALSE,以下情况仍会导致主表被卡:
- 主表上有未提交的大事务(长查询会等其 SCN 推进,表现为
enq: TX - row lock contention) - 物化视图定义里用了
SYSDATE或子查询引用了其他未建日志的表(强制降级为COMPLETE刷新) - 数据库未开启
QUERY REWRITE且 MV 未加ENABLE QUERY REWRITE,优化器可能误选主表而非 MV,放大主表压力 - 刷新作业跑在业务高峰,大量
SELECT /*+ FULL */扫描主表引发 I/O 竞争(建议绑定计划或加PARALLEL提升吞吐)
最易忽略的一点:DBMS_MVIEW.REFRESH 默认以 COMMIT 结束,但若调用方已开启事务,这个隐式 commit 会提前释放锁——务必确认调用上下文是否套在外部事务里。











