根本原因是ATOMIC_REFRESH=TRUE强制单事务TRUNCATE+INSERT,触发DDL级library cache lock阻塞;设FALSE改用DELETE+INSERT并行才生效,且须验证FAST刷新可用性。

为什么并发刷新物化视图会卡住
根本原因不是“多个刷新同时跑”,而是默认 ATOMIC_REFRESH => TRUE 强制所有刷新走单事务路径:先 TRUNCATE 再 INSERT,而 TRUNCATE 是 DDL,必须对物化视图表加独占 library cache lock。只要一个刷新在跑,其他任何查询(甚至查基表的 SQL)都可能被阻塞在 library cache lock 等待上。
常见现象:v$session 里大量会话 event = 'library cache lock',blocking_session 指向某个 J00x 后台进程;v$locked_object 中锁模式为 6(排他锁),对象正是物化视图名。
必须显式关闭 ATOMIC_REFRESH
设为 FALSE 后,Oracle 改用 DELETE + INSERT /*+ APPEND */,锁粒度降为行级(ROW EXCLUSIVE),并发读写基本互不干扰。
- 手动刷新时必须写全参数:
DBMS_MVIEW.REFRESH('MV_NAME', method => 'F', atomic_refresh => FALSE) - 定时任务(
DBMS_SCHEDULER或封装的DBMS_JOB过程)中,调用点必须显式传入atomic_refresh => FALSE,不能依赖默认值 - 副作用真实存在:刷新过程中物化视图会短暂为空(非空闲状态),应用层必须能容忍这个窗口期;否则需配合双读逻辑或分区交换方案
并发前必须验证 FAST 刷新是否真可用
atomic_refresh => FALSE 只对真正支持 FAST 的物化视图有效。如果日志缺失、基表被 ALTER、或定义含子查询/聚合,Oracle 会静默退化为 COMPLETE 刷新——你关了原子性,但锁没少,反而更慢更重。
验证三件事,缺一不可:
-
SELECT fast_refreshable FROM user_mviews WHERE mview_name = 'MV_NAME'必须返回'FAST'(不是'FAST_SNAPSHOT'或'NO') -
SELECT * FROM user_mview_analysis WHERE mview_name = 'MV_NAME'不能有capable_flag = 'N'的项(如MISSING_LOG、NO_PRIMARY_KEY) -
SELECT log_table FROM user_mview_logs WHERE master = 'BASE_TABLE_NAME'结果必须非空,且日志含SEQUENCE()和PRIMARY_KEY(或ROWID)
并行刷新不能替代 ATOMIC_REFRESH => FALSE
并行(parallelism => 4)只在 atomic_refresh => FALSE 时生效。若没关原子性,并行参数完全无效,所有进程挤在一个事务里等提交,锁和 undo 压力反而更大。
启用并行还需额外两步:
- 会话级必须先执行:
ALTER SESSION ENABLE PARALLEL DML(漏掉就全程串行) -
parallel_max_servers要设够:建议 ≥ 单次最大并行度 × 并发刷新任务数(例如同时刷 3 个 MV,每个设parallelism => 4,则至少设为12) -
parallel_adaptive_multi_user必须为FALSE,否则高负载时 Oracle 会自动降级并行度
真正难处理的不是锁本身,而是“你以为改了参数就解了锁”,结果发现物化视图早已因日志失效或子查询退化为 COMPLETE 刷新——此时锁还在,只是换了个更慢的方式挂着。每次调参前,先查 user_mviews 和 user_mview_logs,比反复 kill session 有用得多。


















