atomic_refresh=true必然引发死锁风险,因其将刷新全程置于单事务中:先delete后insert,全程持tx行锁甚至tm表锁,与业务dml争同一行时互等触发ora-00060;刷新越久,锁窗越宽,死锁概率指数上升。

根本原因不是物化视图本身,而是 DBMS_MVIEW.REFRESH 默认启用 atomic_refresh => TRUE,强制走事务级全量刷新路径。
atomic_refresh=TRUE 为什么必然引发死锁风险
这个参数设为 TRUE(默认值)时,Oracle 会把整个刷新包装成一个事务:先 DELETE 旧数据,再 INSERT 新数据。这导致:
- 全程持有基表和物化视图表的
TX行锁,甚至在某些场景下升级为TM表级锁(比如日志缺失退化为 COMPLETE 刷新) - 业务 DML 正好修改同一行时,双方互等对方释放 TX 锁 → 直接触发
ORA-00060 - 刷新耗时越长,锁持有窗口越宽,死锁概率指数级上升;百万级数据下,该事务可能持续几十秒
为什么 FAST 刷新声明 ≠ 实际支持 FAST
很多 DBA 看到创建语句里写了 REFRESH FAST ON COMMIT 就认为安全,但 Oracle 运行时根本不认“写上去的”,只认实际能力。不满足条件时,atomic_refresh => FALSE 也会静默退化为 COMPLETE,反而更慢更锁。
- 验证真支持 FAST:查
user_mviews中fast_refreshable字段必须是'FAST',不能是'FAST_SNAPSHOT'或'NO' - 检查依赖完整性:运行
SELECT * FROM user_mview_analysis WHERE mview_name = 'MV_NAME',重点关注capable_flag = 'N'的项(如缺失主键、远程表、无 MLOG$_xxx 日志) - 确认日志存在且有效:
SELECT log_table FROM user_mview_logs WHERE master = 'BASE_TABLE'必须返回非空结果
并行刷新(PARALLELISM > 0)在 11g/19c 中常起反作用
开并行本意是提速,但在物化视图刷新场景下,它往往放大锁冲突和资源争用。
- 并行进程会同时扫描
MLOG$_xxx日志表,若该表缺少(snaptime$$, sequence$$)复合索引,就会触发大量磁盘排序和临时段争用 -
v$session_longops显示多个Parallel Query卡在Sort Segment,说明并行没加速,反而拖慢了单次刷新 - 建议 OLTP 场景统一设
parallelism => 0;确需并行时,必须先建索引:CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$)
刷新期间短暂空表是 atomic_refresh => FALSE 的硬代价
改用 TRUNCATE + INSERT 路径后,物化视图在 TRUNCATE 完成但 INSERT 尚未结束前,会真实为空。这点极易被忽略:
- 应用层若强依赖“查询必有数据”,此时会直接报空结果或逻辑异常,而非锁等待
- 这不是 bug,是设计使然;无法靠加 hint 或调参绕过
- 若业务无法容忍,必须配合双读逻辑(先查 MV,空则 fallback 查基表)或分区交换方案,不能只改参数











