Oracle 19c 不支持 OUT OF PLACE 刷新,其物化视图刷新始终在原表上执行 DML 或 TRUNCATE/INSERT,不创建隐藏表;atomic_refresh=>FALSE 仅为非原子直写,并非异地切换;手动分区交换或同义词切换是唯一近似替代方案,但需放弃查询重写等核心功能。

Oracle 19c 不支持所谓“OUT OF PLACE”刷新物化视图——这个说法本身是误传,混淆了 OceanBase 或 PostgreSQL 的机制。Oracle 原生物化视图刷新没有 OUT OF PLACE 模式,也不创建隐藏表、不切换表名,更不依赖“异地刷新”逻辑。
为什么 Oracle 19c 没有 OUT OF PLACE 刷新
Oracle 的物化视图刷新本质是直接在原表(MATERIALIZED VIEW 对应的底层段)上执行 DML(如 DELETE + INSERT)或直接重写(TRUNCATE + INSERT /*+ APPEND */),全程锁定该 MV 对象,不引入中间隐藏表。所谓“OUT OF PLACE”是 OceanBase V4.3+ 明确采用的异地刷新策略(创建隐藏表 → 刷新 → 原子切换),而 Oracle 从未实现该模式。
常见误解来源:
- 把
DBMS_MVIEW.REFRESH的atomic_refresh => FALSE参数误认为“out-of-place”——它只是关闭事务原子性,并非切换表; - 将分区交换(
EXCHANGE PARTITION)等手工维护技巧当成内置刷新模式; - 混淆了 Oracle 的
ON COMMIT/ON DEMAND刷新时机与物理刷新方式。
atomic_refresh => FALSE 的真实作用和风险
当你调用 DBMS_MVIEW.REFRESH 并设置 atomic_refresh => FALSE 时,Oracle 会:
- 先
TRUNCATE物化视图基表(不是隐藏表,就是你查的那张 MV 表); - 再用
INSERT /*+ APPEND */直接加载新数据; - 跳过事务封装,不回滚整个刷新动作(失败时只能部分回滚)。
这不是“out-of-place”,而是“非原子直写”。它确实能提升大批量刷新性能,但带来明显副作用:
- 刷新过程中 MV 表为空,查询会返回空结果或报错(取决于隔离级别);
- 无法与其它 DML 并发安全:若刷新中途被中断,MV 处于截断未填充状态,业务不可用;
- 不适用于
ON COMMIT刷新场景(该模式强制atomic_refresh => TRUE)。
示例调用:
EXEC DBMS_MVIEW.REFRESH( list => 'sales_mv', method => 'C', atomic_refresh => FALSE );
Oracle 19c 真正可用的“类 out-of-place”替代方案
如果你需要接近“不阻塞查询 + 快速切换”的效果,唯一可行路径是手动模拟:用普通表 + 分区交换 + 同义词切换。但这完全脱离 DBMS_MVIEW 刷新体系,属于自建物化逻辑:
- 新建一张结构相同的普通表
sales_mv_new,全量构建数据; - 对原 MV 所在表(假设是分区表)执行
EXCHANGE PARTITION,把新数据快速挂载; - 或用同义词(
CREATE OR REPLACE SYNONYM sales_mv FOR sales_mv_new)指向新表(需应用层配合重连或缓存刷新)。
注意:这要求你放弃 Oracle 物化视图的自动依赖管理、日志增量(FAST)、查询重写(QUERY REWRITE)等核心能力。一旦走这条路,就不再是“刷新物化视图”,而是“手动维护汇总表”。
增量刷新(FAST)为何不能绕过锁表
即使你启用 FAST 刷新并建好物化视图日志(MATERIALIZED VIEW LOG),Oracle 仍会对物化视图本身加 ROW EXCLUSIVE 锁(非排他,但阻塞 DDL 和某些 DML)。这是因为:
- 增量过程要读取日志、计算 delta、合并到目标 MV,必须保证 MV 数据一致性窗口;
- Oracle 不提供“并发可读 + 增量写入”的无锁 MV(PG 的
CONCURRENTLY是另一套设计,Oracle 无对应特性); -
FAST刷新依然在原表上做MERGE或小批量INSERT/UPDATE/DELETE,不是创建新对象。
所以,别指望通过换参数或加 hint 实现 Oracle 下的“无感知切换”——它的设计哲学就是强一致性优先,而非可用性妥协。
真正关键的取舍点在于:你要的是 Oracle 原生物化视图的完整功能栈(包括查询重写、自动依赖跟踪、ON COMMIT),还是可以接受放弃这些、用普通表+脚本+分区交换来换取刷新期间的查询连续性。两者无法兼得,也没有隐藏开关能绕过这个根本限制。


















