默认刷新锁基表因atomic_refresh=>true将刷新包在单事务中,先delete后insert,需对基表行加tx锁;若退化为complete则触发tm表锁。

直接设 atomic_refresh => FALSE,配合验证 FAST 可刷性、清理日志和索引优化,才能真正避开全量刷新时对业务表的长时间锁定。
为什么默认刷新会锁住基表
Oracle 11g+ 中 DBMS_MVIEW.REFRESH 默认启用 atomic_refresh => TRUE,整个刷新被包在一个事务里:先 DELETE 旧数据,再 INSERT 新数据。这个过程必须维持一致性读,因此会对基表上所有涉及行加 TX 行锁;若日志缺失或条件不满足导致退化为 COMPLETE 模式,还会触发 TM 表级锁。
常见表现包括:
-
v$locked_object显示基表(如SALES)被锁,locked_mode为 3 或 6 - 业务 DML 报
ORA-00054: resource busy - 刷新卡在
enq: TX或db file sequential read
怎么安全地关掉原子刷新
显式传参 atomic_refresh => FALSE 后,Oracle 改用 TRUNCATE + INSERT /*+ APPEND */ 模式,跳过 UNDO/REDO 记录,只读基表不写锁——这是降低锁窗口最有效的手段。
但必须满足前提:
- 确认物化视图真支持 FAST:
SELECT fast_refreshable FROM user_mviews WHERE mview_name = 'MV_SALES'返回值必须是FAST,不能是DIRLOADDML或UNDEFINED - 若含 LOB 字段(
CLOB/BLOB),FAST 不可用,atomic_refresh => FALSE仅能用于 COMPLETE 刷新,此时 MV 会短暂不可用(报ORA-00942) - 调用时务必显式指定:
DBMS_MVIEW.REFRESH('MV_SALES', atomic_refresh => FALSE),别依赖默认值
为什么并行刷新反而加重锁争用
很多人以为开 PARALLELISM > 0 能提速,但在 11g 中它常适得其反:多个并行进程同时扫描 MLOG$_xxx 日志表,而该表是普通堆表,若缺少 (snaptime$$, sequence$$) 复合索引,就会触发大量磁盘排序与临时段争用,间接延长持锁时间。
典型症状:
-
v$session_longops显示大量Parallel Query卡在Sort Segment -
v$lock中多个会话对同一MLOG$_xxx表持有 TX 锁 - 基表 DML 响应变慢,即使没直接冲突,也因 UNDO 回滚段高并发写入而延迟
实操建议:
- OLTP 场景下优先设
PARALLELISM => 0 - 确需并行(如夜间大分区刷新),先建索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$) - 索引字段顺序不能颠倒:
snaptime$$必须在前
容易被忽略的三个细节
哪怕参数都设对了,以下三点仍可能让锁问题重现:
-
MLOG$_xxx表统计信息过期,导致优化器误选执行计划,引发全表扫描——必须定期跑DBMS_STATS.GATHER_TABLE_STATS,且estimate_percent => 100更稳 - 日志积压严重:
SELECT COUNT(*) FROM MLOG$_SALES WHERE snaptime$$ 若结果很大,说明未及时清理,应执行 <code>DBMS_MVIEW.PURGE_LOG('SALES', 1) -
ATOMIC_REFRESH => FALSE下 MV 刷新期间为空,应用若没做双读容错或兜底逻辑,会直接报错,这不是数据库问题,而是调用方没适配语义变更











