PostgreSQL 16中加CONCURRENTLY仍报“does not have a unique index”是因为命令入口即校验硬性前提:必须显式在物化视图上创建覆盖全部分组列、全NOT NULL、无函数表达式的VALID唯一索引,缺一则直接拒绝执行,不进入刷新阶段。

PostgreSQL 16 不支持真正的增量刷新,REFRESH MATERIALIZED VIEW CONCURRENTLY 仍是全量执行 + 差集比对,但能避免读锁;它能跑起来的唯一前提是:物化视图上必须有满足全部硬性条件的唯一索引。
为什么加了 CONCURRENTLY 还报 “does not have a unique index”
这不是配置漏了、权限不够,而是 PostgreSQL 16 在命令入口就做校验:没索引,直接拒掉,根本不会进执行阶段。错误信息固定为:ERROR: cannot refresh materialized view "xxx" concurrently, because it does not have a unique index。
- 索引必须显式建在物化视图上,
CREATE UNIQUE INDEX ON my_mv (id)—— 源表有主键、或约束带唯一性,都不算数 - 所有索引列必须
NOT NULL;如果字段允许 NULL,得用COALESCE(col, 0)或类似兜底后再建索引 - 聚合类视图(如含
GROUP BY tenant_id, event_type)必须把全部分组列放进索引,顺序也要一致,(tenant_id, event_type)不能写成(event_type, tenant_id) - 刚建完索引可能状态是
INVALID,需执行一次VACUUM my_mv或ANALYZE my_mv才被识别 - 表达式索引(如
(UPPER(name)))、带WHERE条件的索引、函数索引,一律不被接受
CONCURRENTLY 刷新失败的常见真实原因
即使索引建对了,刷新仍可能中途失败,且失败后物化视图处于“部分更新”状态 —— 旧数据没全删、新数据没全写,不可靠。
- 业务写入与新数据产生唯一键冲突:
ERROR: duplicate key value violates unique constraint(比如新数据要插order_id = 123,但业务刚好 INSERT 了同 ID 的行) - 某行在 merge 阶段前被业务 UPDATE 过:
could not lock updated tuple in materialized view - 物化视图刚创建、还没填充过(即处于
WITH NO DATA状态),CONCURRENTLY会直接拒绝 —— 必须先用普通REFRESH MATERIALIZED VIEW my_mv填一次 - 定义中含不可重放表达式:
now()、random()、CURRENT_USER,或未配ORDER BY的DISTINCT ON/ 窗口函数,导致新旧结果无法精确比对
性能和资源上容易被低估的开销
并发刷新不是“更快”,而是“可读”,实际代价往往更高。
- 每次都要全量扫描源表 + 全量执行原始查询 + 两次索引扫描(新旧数据各一次)+ 差集计算,实测 400 万行下比普通刷新慢 3–4 倍
- 临时副本需额外磁盘空间,高频刷新(如每分钟一次)极易触发
no space left on device - 它不阻塞
SELECT,但会阻塞某些 DML:比如对正在刷新的 MV 执行UPDATE或DELETE可能被锁住 - 真正省时间的地方不在刷新命令本身,而在物化视图定义:去掉
DISTINCT ON、避免无序json_agg、让聚合字段类型和 WHERE 条件严格一致(比如别用EXTRACT(YEAR FROM ts) = 2023,该写EXTRACT(YEAR FROM ts)::int = 2023)
想“增量”,只能自己控范围
PostgreSQL 内核不提供基于 WAL 或变更日志的增量能力。CONCURRENTLY 不是增量,只是“全量查 + 全量比 + 差异写”。真要减少数据量,得靠外部逻辑:
- 手动维护位点:建一张
matview_refresh_state表存上次最大updated_at,刷新 SQL 中加WHERE updated_at > ? - 用
pg_matview扩展:本质是把 MV 当普通表 + 触发器捕获变更 + 手写INSERT ON CONFLICT,它不自动推导变化行,只帮你搭个壳 - 前提是你有可靠单调递增字段(如自增 ID、严格递增时间戳),否则边界判定会出错
最常被忽略的一点:索引结构必须在建物化视图时就设计好,而不是等 CONCURRENTLY 报错才回头改。一旦 MV 数据量上去,重建索引或改结构的成本远高于初期多花十分钟选对主键语义。











