PostgreSQL 16 的 REFRESH MATERIALIZED VIEW 仍是全量执行,无内核级增量刷新;CONCURRENTLY 要求唯一非空索引且覆盖 GROUP BY 列,实际开销常高于非并发刷新,性能优化关键在于物化视图定义和统计信息更新。

REFRESH MATERIALIZED VIEW 仍是全量执行,别指望它自动增量
PostgreSQL 16 没引入真正的增量刷新机制。REFRESH MATERIALIZED VIEW 无论加不加 CONCURRENTLY,底层都重新执行原始 SELECT 查询,生成全新结果集。所谓“增量”只能靠外部手段模拟,比如用 pg_matview 扩展、或自己维护时间戳/序列位点 + WHERE updated_at > last_refresh 过滤条件。内核层面没变化,这点和 15 及之前版本一致。
CONCURRENTLY 刷新失败的三个硬门槛必须踩准
想用 REFRESH MATERIALIZED VIEW CONCURRENTLY 避免读锁,但总报 ERROR: cannot refresh materialized view "xxx" concurrently because it does not have a unique index?不是索引没建,而是没建对。必须同时满足:
- 物化视图上显式创建了
UNIQUE或PRIMARY KEY索引(基表主键不会继承) - 索引列全部为
NOT NULL;若源字段允许NULL,得先COALESCE(col, 0)再建索引 - 索引覆盖全部
GROUP BY列(顺序也要一致,(a, b)索引不能用于GROUP BY b, a)
缺一不可,否则直接拒绝执行,连试都不试。
CONCURRENTLY 不等于“无代价”,反而更容易拖慢刷新
很多人以为加了 CONCURRENTLY 就只是“多花点时间换并发”,实际更麻烦:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 它先全量跑一遍原始查询,再逐行比对旧数据做
INSERT/UPDATE/DELETE,I/O 和 CPU 开销常比非并发高 3–5 倍 - 刷新期间仍持有
SHARE UPDATE EXCLUSIVE锁,DROP、ALTER、TRUNCATE都会被阻塞 - 业务写入若触发唯一键冲突(如新插入行 key 与即将更新的行相同),会直接报错中断
- 每次刷新临时多占一倍磁盘空间,高频刷新(如每分钟)容易撑爆
pg_wal或base目录
400 万行实测:非并发刷新 32 秒,CONCURRENTLY 耗时 147 秒且 pg_stat_activity 显示大量 Hash Join 和 Sort —— 这不是配置问题,是机制决定的。
真正能提速的刷新策略,往往在 SQL 定义里
刷新快慢,80% 取决于物化视图定义本身是否可优化:
- 避免在
SELECT中用DISTINCT ON、窗口函数或复杂子查询,它们会让CONCURRENTLY失效,甚至导致回退到全量扫描比对 - 聚合字段类型必须和 WHERE 条件严格对齐:比如
EXTRACT(YEAR FROM order_date)返回double precision,就不能写WHERE year = 2023(整型),否则隐式转换废掉索引 - 如果日增数据稳定且小于总量 5%,
CONCURRENTLY可接受;否则宁可调度到低峰期用非并发刷新,也别硬扛高负载
最常被忽略的一点:刷新前没 ANALYZE 物化视图本身,统计信息过期会导致比对逻辑走错执行计划——这比选错刷新方式影响还大。

















