并发刷新卡住主因是争抢基表行锁、MLOG$_xxx日志行及library cache lock(atomic_refresh=TRUE时);必须显式设atomic_refresh=>FALSE降为行级锁,并确保FAST刷新真实可用,否则静默退化为更慢的COMPLETE刷新。

为什么并发刷新物化视图会卡住
不是因为“多个REFRESH命令互相锁”,而是每个刷新事务都在争抢同一组底层资源:物化视图基表的行锁、物化视图日志(MLOG$_xxx)的变更记录行、以及library cache lock(当atomic_refresh => TRUE时)。典型表现是v$session里大量会话停在event = 'library cache lock'或enq: TX - row lock contention,且blocking_session指向某个DBMS_MVIEW.REFRESH会话。
必须关掉atomic_refresh => TRUE
这是并发安全的第一道门槛。默认值TRUE会让每次刷新走TRUNCATE + INSERT路径,TRUNCATE是DDL操作,强制加exclusive library cache lock——所有查该物化视图、甚至查其基表的SQL都会被拦住。设为FALSE后改用DELETE + INSERT,锁粒度降为ROW EXCLUSIVE,业务查询和DML基本不受影响。
- 调用必须显式传参:
DBMS_MVIEW.REFRESH('MV_SALES', method => 'F', atomic_refresh => FALSE),不能依赖默认值 - 前提条件必须满足:物化视图真支持FAST刷新(查
user_mviews.fast_refreshable = 'FAST'),且对应日志存在并含ROWID和所有SELECT列 - 若不满足FAST条件,此参数会静默退化为COMPLETE刷新,反而更慢更锁——这不是配置失效,是能力缺失
避免多个刷新任务同时撞上同一张基表
即使atomic_refresh => FALSE,多个FAST刷新仍可能因争抢MLOG$_xxx里的同一批变更记录而死锁。尤其当基表正被高频UPDATE/INSERT时,日志表本身就成了瓶颈。
- 错峰执行:把不同物化视图的定时任务分散在5–10分钟窗口内,避开每小时整点这种常见调度高峰
- 按分区刷:如果基表按时间分区(如
PARTITION BY RANGE(order_date)),刷新时指定refresh_after_commit => TRUE并配合分区键WHERE条件,只处理当日/昨日分区 - 检查日志表膨胀:运行
SELECT segment_name, bytes/1024/1024 MB FROM dba_segments WHERE segment_name LIKE 'MLOG$%',若超1GB,说明日志未及时消费,需排查刷新任务是否长期失败或被阻塞
ORA-00060死锁发生后怎么快速收场
一旦出现ORA-00060,Oracle已写好trace文件,里面包含完整的deadlock graph。别猜,直接看它。
- 先定位源头:运行
SELECT object_name, locked_mode FROM v$locked_object lo, dba_objects ao WHERE lo.object_id = ao.object_id,重点看锁在MV_XXX或MLOG$_YYY上的行 - 查锁持有者:
SELECT sid, serial#, sql_id, event, blocking_session FROM v$session WHERE sid IN (SELECT session_id FROM v$locked_object),确认是后台作业(program = oracle@ (J000))还是业务应用 - 若确定是刷新任务卡死,且无其他依赖,直接中止:
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;但别杀正在跑大事务的业务会话,它可能是间接阻塞源 - 事后必须查trace:路径由
SELECT value FROM v$diag_info WHERE name = 'Diag Trace'返回,找最新_ora_*.trc文件,搜索DEADLOCK段——那里有谁锁了谁、持有什么锁、等待什么锁的完整链条
并发刷新真正的难点不在语法,而在对物化视图日志生命周期和基表DML节奏的感知。一个没被清理的MLOG$_表,比十个并发REFRESH更容易让系统停摆。


















