增量刷新不是数据库自动感知变化,而是依赖显式配置:oracle需手建含正确列和选项的物化视图日志,postgresql的concurrently实为全量查询+upsert合并,mysql/oceanbase原生不支持,真增量需pg_ivm等扩展或手动实现。

增量刷新不是数据库自动“感知变化”,而是靠你显式建好配套结构、写对查询逻辑,才能触发。否则 REFRESH FAST 或 CONCURRENTLY 会静默退化为全量刷新——不报错,但性能崩了。
Oracle 必须手建 MATERIALIZED VIEW LOG,且列名/选项一个都不能错
Oracle 的增量刷新依赖 MLOG$ 表记录变更,但它不会自动推导要记哪些字段。你得按物化视图的 SELECT 反向拆解出所有原始列(不是别名、不是函数结果),再显式声明。
-
WITH PRIMARY KEY:即使 MV 里没用到主键,只要含聚合就必须加,否则拒绝 FAST -
SEQUENCE(col1, col2):列出所有参与GROUP BY、WHERE、SUM()、JOIN的原始列名,比如TRUNC(create_time)对应日志列只能是create_time -
INCLUDING NEW VALUES:漏掉它,UPDATE 就无法拆成“删旧 + 插新”,增量逻辑直接失效 - 大小写必须完全一致:日志建
PAY_STATUS,MV 里写pay_status→ 匹配失败,退化 COMPLETE
错误示例:CREATE MATERIALIZED VIEW LOG ON t_order 这种空壳语句建出来的日志表,字段为空、无 SEQUENCE$$、不存新值,后续所有 REFRESH FAST 全部静默走全量。
PostgreSQL 的 CONCURRENTLY 不是真增量,而是 upsert 合并
PostgreSQL 没有 Oracle 那套日志机制,REFRESH MATERIALIZED VIEW CONCURRENTLY 实际行为是:全量执行一遍 MV 定义查询 → 得到新结果集 → 用唯一索引比对旧数据 → 插入新增行、删除旧行。它不读变更日志,也不跳过未改行。
- 必须在 MV 上建
UNIQUE INDEX,且覆盖所有输出列,否则报错cannot refresh materialized view "xxx" concurrently because it does not have a unique index - 哪怕源表只改了 1 行,整个查询仍全量跑一遍,只是不锁表而已
- 真正只查变更数据,得自己写条件,比如
WHERE updated_at > (SELECT max(updated_at) FROM mv_cache) - 失败风险更高:CONCURRENTLY 失败可能已插入部分新行但没删完旧行,导致重复或缺失,需人工干预
MySQL 和 OceanBase 原生不支持增量刷新,得靠手动模拟
MySQL 根本没有物化视图语法,OceanBase 虽支持 REFRESH FAST,但限制极严:不支持 MAX/MIN、HAVING、ORDER BY、窗口函数,GROUP BY 只认 SUM/COUNT 且不能嵌套表达式。
- MySQL 常用方案:建普通表 +
INSERT ... ON DUPLICATE KEY UPDATE+ 定时任务,靠last_updated时间戳过滤增量 - OceanBase 若用
REFRESH FAST失败,DBMS_MVIEW.EXPLAIN_MVIEW会显示REFRESH_METHOD = 'COMPLETE',但不会提示具体哪条规则不满足 - 所有手动方案都要注意基表结构变更同步:比如源表加字段,物化表也得
ALTER TABLE,否则 INSERT 报错
pg_ivm 插件才是 PostgreSQL 真正的增量更新方案
如果真要基表一改、MV 立刻同步,pg_ivm 是目前 PostgreSQL 生态里最接近 Oracle FAST 的选择。它通过在基表上建 AFTER 触发器,把变更实时应用到 MV,不依赖全量重算。
- 安装后需
CREATE EXTENSION pg_ivm,且基表必须有主键或唯一约束 - 刷新时机可选立即(事务内)或延迟(访问时/定时),不像
CONCURRENTLY那样必须手动调 - 不兼容复杂查询:含
UNION、子查询、DISTINCT的 MV 无法启用 IVM - 触发器开销真实存在,高频写场景需压测验证
真正难的不是写那几行 SQL,而是搞清你的 MV 查询到底依赖哪些原始字段、数据库版本是否支持对应特性、以及失败时怎么快速定位是日志缺列还是聚合函数越界——这些细节错一个,增量就没了。










