默认全量刷新会阻塞所有select及基表dml,concurrently需唯一索引且资源开销更大,fast依赖物化视图日志完备性与sql限制,刷新频率须依据变更速率、mv增长斜率和耗时趋势动态调整。

大规模并发环境下,物化视图的刷新频率不是“要不要调”的问题,而是“不调就会拖垮查询或写入”的临界点。核心矛盾在于:刷新操作本身会争抢资源,而业务查询和基表DML也在同时争抢——三者共用同一套I/O、CPU和锁机制。
REFRESH MATERIALIZED VIEW 会阻塞什么?
默认全量刷新(REFRESH MATERIALIZED VIEW mv_name)会获取排它锁(AccessExclusiveLock),导致:
- 所有对该物化视图的
SELECT被阻塞,直到刷新完成 - 所有对基表的
INSERT/UPDATE/DELETE也可能被间接阻塞(尤其当基表有触发器或外键引用该MV时) - 在高QPS报表服务中,一次2秒的刷新可能引发数十个查询排队超时
CONCURRENTLY 刷新真的不卡查询吗?
使用 REFRESH MATERIALIZED VIEW CONCURRENTLY mv_name 确实避免了读阻塞,但代价是:
- 必须存在唯一索引(如主键或
UNIQUE约束),否则报错:ERROR: cannot refresh materialized view "mv_name" concurrently because it does not have a unique index - 刷新过程实际是“创建新副本 + 原子替换”,内存与临时空间开销翻倍,容易触发OOM或磁盘IO瓶颈
- 在数据量超千万行时,
CONCURRENTLY可能比全量刷新慢2–3倍,反而拉长资源占用窗口
增量刷新(FAST)为什么不能随便开?
金仓或Oracle系的 REFRESH FAST 表面高效,但依赖严格前提:
- 基表必须提前建好物化视图日志(
CREATE MATERIALIZED VIEW LOG ON table_name WITH PRIMARY KEY),且日志格式(PRIMARY KEY/ROWID/CTID)必须与MV定义完全匹配 - 物化视图SQL中若含
ORDER BY、LIMIT、非确定性函数(如now()、random()),会导致FAST不可用,自动降级为全量 - 多个并发
FAST刷新请求会竞争日志表(mlog$_xxx)的行锁,反而形成新的热点
定时刷新间隔怎么定才不踩坑?
不能只看“业务能接受几分钟延迟”,更要盯住三个真实指标:
-
基表变更速率:如果每秒新增1000条订单,而你每小时刷一次,MV日志表可能膨胀到GB级,下次
FAST刷新直接卡死 -
物化视图大小增长斜率:用
pg_total_relation_size('mv_name')监控,若周环比增长>40%,说明当前刷新频次已无法消化增量 -
刷新耗时趋势:记录每次
REFRESH执行时间,若连续3次增长超30%,大概率是日志未清理或索引失效,需介入而非简单调低频次
真正难的不是算出“该每5分钟刷一次”,而是当pg_stat_activity里突然出现十几个refresh materialized view进程长时间处于active状态时,你得立刻判断:是基表锁住了日志写入?还是MV上缺失唯一索引导致并发刷新退化成串行?这些细节一旦忽略,再精细的定时策略也会在凌晨三点崩给你看。











