atomic_refresh => FALSE是绕过基表锁的关键开关,需配合物化视图真实支持FAST刷新(fast_refreshable='FAST')、日志表建索引(snaptime$$,sequence$$)及统计信息全采样,否则会静默退化为更慢的COMPLETE刷新。
atomic_refresh => FALSE 是绕过基表锁的关键开关
oracle 默认用 atomic_refresh => true 刷新物化视图,本质是把整个刷新包在一个事务里:先 delete 旧数据,再 insert 新数据。这个过程必须维持一致性读,会对基表涉及的行加 tx 锁,高并发 dml 碰上就卡在 enq: tx - row lock contention。设为 false 后,oracle 改用 truncate + insert /*+ append */,跳过事务日志,只读基表、不写锁——但前提是物化视图真支持 fast 刷新。
- 查真实支持状态:
SELECT fast_refreshable FROM user_mviews WHERE mview_name = 'MV_SALES',返回值必须是FAST,不是DIRLOADDML或UNDEFINED - 若返回非
FAST,atomic_refresh => FALSE会静默退化为COMPLETE,反而更慢更锁 -
ON COMMIT刷新下该参数无效——它强制同步执行MERGE,和业务事务共享锁资源
物化视图日志(MLOG$_xxx)没索引,FAST 刷新就变慢刷
快速刷新要扫 MLOG$_SALES 表取变更记录,但日志表是普通堆表,缺索引就会全表扫描+磁盘排序,持锁时间拉长,间接拖累基表 DML。典型表现是 v$session_longops 卡在 Sort Segment,v$lock 里多个会话争同一张日志表。
- 必须建复合索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$),字段顺序不能颠倒 - 索引建完立刻更新统计信息:
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'MLOG$_SALES', estimate_percent => 100) - 定期清理积压:
EXEC DBMS_MVIEW.PURGE_LOG('SALES', 1)清 1 天前记录,避免日志膨胀拖慢扫描
并行刷新(PARALLELISM > 0)在 11g 中多数时候适得其反
很多人以为开并行能加速,但在 Oracle 11g 中,PARALLELISM > 0 常引发临时段争用和锁冲突。并行进程同时读 MLOG$_xxx,而日志表没索引时,每个进程都做磁盘排序,undo 回滚段被高频写入,基表 DML 响应变慢,即使没直接行冲突也会延迟。
- OLTP 场景优先设
PARALLELISM => 0;尤其当基表 I/O 已是瓶颈时,并行只会加剧争抢 - 确需并行(如夜间大分区刷新),必须先建好
snaptime$$, sequence$$索引,否则并行反而更慢 - 别信默认采样率——
GATHER_TABLE_STATS必须用estimate_percent => 100,否则优化器仍可能选错执行计划
调度刷新时漏掉超时控制,等于埋定时炸弹
DBMS_SCHEDULER 能把刷新移到后台执行,但它不解决锁问题,只把阻塞转移给 job 进程。一个卡住的 COMPLETE 刷新可能 hang 40 分钟,导致后续 17 个任务堆积,最终拖垮调度链。
- 必须显式配置
max_run_duration => '+000 00:30:00',防 LOB 多或统计信息过期导致无限等待 -
max_failures => 1,失败立即告警,避免静默重试刷库 -
job_action中必须显式传参:DBMS_MVIEW.REFRESH('MV_SALES', method => 'F', atomic_refresh => FALSE),不能依赖默认值
真正难的不是“要不要锁”,而是锁多久、锁哪几行、谁来承担短暂不可用。比如 atomic_refresh => FALSE 后物化视图会空几秒,应用层若没双读逻辑,查询就直接报错——这点常被 DBA 忽略,却由下游服务买单。


















