根本原因是rac多实例争抢ji enqueue、mlog$_表行锁、undo分布不均及atomic_refresh=true触发跨节点tm锁同步;需错峰调度、建mlog$复合索引、设atomic_refresh=false并确保fast可刷新。
根本原因不是“刷新本身要锁”,而是 rac 下多个实例同时争抢同一组底层资源:ji enqueue、mlog$_ 表行锁、undo 分布不均,以及默认的 atomic_refresh=true 触发跨节点 tm 锁同步。
为什么 RAC 中 JI-contention 比单实例更严重
RAC 多节点调度 DBMS_JOB 或 DBMS_SCHEDULER 时,若刷新时间未错开(比如都用 SYSDATE + 1/24),极易在秒级精度内触发多个实例并发执行同一物化视图的 DBMS_MVIEW.REFRESH。Oracle 对每个 MV 刷新强制串行化,靠 JI enqueue 实现——该锁是全局队列(GCS/GES 资源),一旦两个实例同时申请,必有一个卡在 enq: JI - contention,v$ges_enqueue 中 REQUESTED ≠ GRANT_LEVEL 可直接确认。
- 查重叠 JOB:
SELECT job, what, next_date, interval FROM dba_jobs WHERE what LIKE '%dbms_mview.refresh%',重点看next_date是否密集扎堆在同秒内 - 单实例可容忍的调度抖动,在 RAC 下会放大为跨节点协调开销,延迟从毫秒级升至百毫秒以上
- 别依赖
DBMS_REFRESH.MAKE自动分组——它不感知 RAC 实例拓扑,组内 MV 刷新仍可能被不同实例并行拉起
atomic_refresh=TRUE 在 RAC 下会升级为 TM 全局锁
默认 atomic_refresh => TRUE 会先 TRUNCATE 物化视图基表,再 INSERT /*+ APPEND */。TRUNCATE 是 DDL,在 RAC 中需广播 TM 锁(enq: TM - contention),所有实例必须达成一致才能继续。哪怕只刷几万行,这个全局协商过程也会阻塞其他实例对该表的任何 DML 和查询。
-
v$locked_object中锁模式为 6(EXCLUSIVE)且object_name是物化视图表名,基本可断定是此问题 - 设为
atomic_refresh => FALSE后改走 DELETE+INSERT 路径,只持 TX 行锁,不再触发 TM 全局锁广播 - 但前提是物化视图真支持 FAST 刷新:
SELECT fast_refreshable FROM user_mviews WHERE mview_name = 'MV_X'必须返回FAST,否则静默退化为 COMPLETE,反而更慢更锁
MLOG$_ 表成为 RAC 行锁热点
FAST 刷新依赖物化视图日志(MLOG$_XXX)消费。该表是普通堆表,默认无高效索引。RAC 多实例并行扫描时,snaptime$$ 和 sequence$$ 字段缺乏复合索引,导致各实例反复争抢同一数据块的 TX 锁,v$lock 中可见多个会话对同一 MLOG$_XXX 持有 TX 锁,v$session_longops 显示大量 Parallel Query 卡在 Sort Segment。
- 必须建索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$) - 禁用并行刷新(
parallelism => 0)比盲目开并行更稳,尤其当基表 I/O 已是瓶颈时 - 检查日志是否失效:
SELECT log_table FROM user_mview_logs WHERE master = 'SALES',返回为空则 FAST 刷新必然失败
undo 表空间分布不均加剧等待链
RAC 中 undo 数据按实例本地分配,但物化视图刷新事务可能跨实例读取一致性镜像(如刷新 MV_A 时需读 MV_B 的最新快照)。若某实例 undo 表空间压力大或响应慢,其他实例在构造 CR 块时会长时间等待 gc cr block busy,表现为刷新卡住、v$session.event 停留在该等待上。
- 监控各实例 undo 使用率:
SELECT inst_id, tablespace_name, status, sum(bytes)/1024/1024 mb FROM gv$undostat GROUP BY inst_id, tablespace_name, status - 避免在 RAC 中使用
SELECT FOR UPDATE NOWAIT做乐观锁兜底——NOWAIT 不跳过全局锁协商,反而因快速失败重试放大 GC 流量 - 若业务允许,将高频刷新 MV 迁移到单一实例(通过
INSTANCE_ID绑定 JOB),减少跨节点协调
真正难处理的从来不是“怎么刷”,而是“谁在什么时候、以什么方式、在哪台实例上刷”。RAC 下的物化视图刷新,本质是把单机的事务协调问题,放大成了多节点的资源仲裁问题——参数调得再细,也绕不开调度错峰、锁粒度收窄、日志索引加固这三件事。











