atomic_refresh => FALSE 是绕过阻塞的首选动作,通过改用 DELETE+INSERT(FAST)或 TRUNCATE+INSERT(COMPLETE)降低锁粒度,但需满足 FAST 刷新能力、日志完备、无聚合/连接等前提条件。

atomic_refresh => FALSE 是绕过阻塞的首选动作
默认 atomic_refresh => TRUE 会让 DBMS_MVIEW.REFRESH 先 TRUNCATE 再 INSERT,触发独占库缓存锁(library cache lock),所有查该物化视图或其基表的会话都会卡住。设为 FALSE 后改走 DELETE + INSERT(FAST)或 TRUNCATE + INSERT /*+ APPEND */(COMPLETE),锁粒度降为行级或仅短暂表级不可读,业务 DML 和查询基本不受影响。
必须满足的前提条件:
-
FAST刷新:查user_mviews确认fast_refreshable = 'FAST',不能是'FAST_SNAPSHOT'或'DIRLOADDML' - 日志完备:执行
SELECT log_table FROM user_mview_logs WHERE master = 'YOUR_BASE_TABLE',结果非空且对应日志表存在 - 无聚合/连接:含
SUM、JOIN、子查询等结构时,atomic_refresh => FALSE会被静默忽略,退化为默认行为
调用示例:DBMS_MVIEW.REFRESH('MV_SALES', method => 'F', atomic_refresh => FALSE)
刷新前必须验证 FAST 刷新能力是否真实生效
创建语句里写了 REFRESH FAST 不代表 Oracle 运行时真认可。很多阻塞问题源于“以为能 FAST,实则 fallback 到 COMPLETE”,而 COMPLETE + atomic_refresh => TRUE 就是锁表元凶。
三步验证缺一不可:
- 查
user_mviews:SELECT mview_name, fast_refreshable FROM user_mviews WHERE mview_name = 'MV_SALES',返回值必须是纯字符串'FAST' - 查
user_mview_analysis:SELECT capability_name, capable_flag FROM user_mview_analysis WHERE mview_name = 'MV_SALES' AND capable_flag = 'N',重点关注REFRESH_FAST_AFTER_INSERT、REFRESH_FAST_AFTER_ONETAB_DML是否为N - 确认日志可用:
SELECT * FROM MLOG$_SALES WHERE ROWNUM 能查出数据,且 <code>SNAPTIME$$不是远古时间(如4000-01-01)
即使开了 atomic_refresh => FALSE,长耗时仍会引发间接阻塞
atomic_refresh => FALSE 解决了 DDL 级锁,但没解决大事务本身的资源压力。若刷新本身要几十秒,DELETE 和 INSERT 会产生大量 undo,其他会话读取旧版本数据时需构造一致性读镜像,可能卡在 read consistency 等待,现象和锁表几乎一样。
监控与应对要点:
- 查真实耗时:
SELECT start_time, end_time, elapsed_seconds FROM user_mview_refresh_times WHERE name = 'MV_SALES' ORDER BY start_time DESC,连续几次超 5 秒就要干预 - 避开业务高峰:尤其避免与批量导入、报表生成、ETL 作业重叠
- 分区表可拆分:对按时间分区的大表,不要一次刷全量,改用
list => 'MV_NAME'配合预置SNAPTIME$$控制范围,单次只刷一个分区 - LOB 列要特别小心:哪怕定义里只带一个
CLOB,COMPLETE 刷新就会启用物理复制路径,undo 消耗翻倍
定时任务中漏传 atomic_refresh 参数是常见盲区
很多人在 DBMS_SCHEDULER 或 DBMS_JOB 里封装了刷新逻辑,但调用时没显式传参,结果还是走默认 TRUE。这类问题往往在上线后才暴露,因为测试环境数据量小、锁不明显。
检查与修复方式:
- 查调度任务内容:
SELECT job_name, job_action FROM dba_scheduler_jobs WHERE job_action LIKE '%DBMS_MVIEW.REFRESH%',确认语句里是否含atomic_refresh => FALSE - 不要依赖存储过程默认值:如果封装了
refresh_mv(p_mv_name VARCHAR2),必须在里面显式写死参数,而不是靠调用方传入 - RAC 环境下注意 job 分布:一个 job 可能在任意节点执行,但锁是全局的;确保所有节点都部署了相同逻辑
最易被忽略的一点:atomic_refresh => FALSE 下,刷新过程中物化视图会短暂为空(TRUNCATE 后、INSERT 前),应用层若没做空结果兜底,会直接报 ORA-01403 或 ORA-08103。这不是锁,是设计契约——得业务自己扛。


















