在报表类系统里,运营往往要求看到“近一分钟成交额”“当前在线设备数”这类指标。如果每次请求都去扫描几十 GB 的明细表做聚合,数据库很容易被打垮。PostgreSQL 提供的物化视图(Materialized View)可以把聚合查询的结果物理存储下来,后续读取直接访问这张“预计算表”,从而避免重复计算。

一、物化视图的基本用法
物化视图和普通视图最大的区别在于:普通视图只是保存了一段 SQL,每次查询都重新执行;而物化视图在创建时会把查询结果真正写入磁盘,形成一张只读表。我们可以利用它来缓存聚合数据。
下面这段 SQL 创建了一个按小时汇总的物化视图,统计订单表的成交额与订单数:
CREATE MATERIALIZED VIEW mv_hourly_order AS
SELECT
date_trunc('hour', created_at) AS hour_slot,
shop_id,
SUM(amount) AS total_amount,
COUNT(*) AS order_cnt
FROM orders
GROUP BY date_trunc('hour', created_at), shop_id;
-- 为后续并发刷新建立唯一索引
CREATE UNIQUE INDEX uk_mv_hourly ON mv_hourly_order (hour_slot, shop_id);
创建之后,查询报表只需读取 mv_hourly_order,而不再触碰原始的 orders 大表。对于按小时、按天汇总的静态报表,这种方式能降低至少一个数量级的响应时间。
不过物化视图里的数据并不会随基表自动更新,必须显式执行刷新命令。理解刷新方式,是做到“近实时”的关键。
二、全量刷新与并发刷新
最简单的刷新是 REFRESH MATERIALIZED VIEW,它会锁住视图并重新跑一遍底层查询,在刷新期间视图不可读,且数据量越大越慢。对于近实时场景,这种“卡顿式”刷新很难接受。
PostgreSQL 9.4 之后支持 CONCURRENTLY 选项,它可以在不阻塞读取的前提下增量更新。前提是物化视图上必须存在唯一索引。其原理是对比基表与视图的差异,只插入、更新变化的部分。
-- 并发刷新,不阻塞查询,但更耗资源 REFRESH MATERIALIZED VIEW CONCURRENTLY mv_hourly_order;
并发刷新虽然友好,但也有代价:它需要做表级扫描来比对,CPU 和 I/O 开销明显高于全量刷新。如果基表写入非常频繁,过于密集的 CONCURRENTLY 刷新会导致数据库负担加重,甚至引发膨胀。
实践中通常结合业务节奏,例如每 30 秒或 1 分钟触发一次并发刷新,把数据延迟控制在可接受范围内,而非追求绝对实时。
三、降低延迟的辅助手段
仅靠定时刷新,最短延迟也受刷新周期限制。若需要秒级精度,可以引入触发器或逻辑解码(Logical Decoding)来捕获变更,异步更新聚合中间表,再让物化视图基于中间表做轻量汇总。
另一种常见架构是“分层聚合”:先用一个普通表实时记录分钟级增量,再用物化视图把分钟表压缩成小时表。这样物化视图的刷新成本极低,而前端查询分钟表获得近实时值。
-- 实时增量表(普通表,由写入服务同时更新)
INSERT INTO rt_minute_stat (minute_slot, shop_id, amount, cnt)
VALUES (date_trunc('minute', NOW()), 1001, 19.9, 1)
ON CONFLICT (minute_slot, shop_id)
DO UPDATE SET amount = rt_minute_stat.amount + EXCLUDED.amount,
cnt = rt_minute_stat.cnt + EXCLUDED.cnt;
-- 物化视图只基于轻量的分钟表汇总
CREATE MATERIALIZED VIEW mv_shop_today AS
SELECT shop_id, SUM(amount) AS day_amount, SUM(cnt) AS day_cnt
FROM rt_minute_stat
WHERE minute_slot >= date_trunc('day', NOW())
GROUP BY shop_id;
这种方案把“高频写”和“重聚合”解耦,既保证了近实时,也避免了物化视图直接面对原始流水表的膨胀问题。同时,分钟表可以定时归档,控制存储规模。
四、避坑与性能要点
第一个坑是忘记建唯一索引却使用 CONCURRENTLY,数据库会直接报错。第二个坑是并发刷新频繁导致物化视图本身产生死元组,需要像普通表一样定期 VACUUM。
此外,如果聚合维度经常变化(例如新增“地区”维度),物化视图需要重建,运维成本较高。此时可以考虑使用物化视图配合应用层缓存,或者改用 CTE 与索引覆盖扫描做折中。
| 方案 | 延迟 | 读性能 | 写负担 |
|---|---|---|---|
| 直接聚合基表 | 实时 | 差 | 无额外 |
| 物化视图全量刷新 | 分钟级 | 优 | 刷新时高 |
| 物化视图并发刷新 | 秒~分钟级 | 优 | 中等 |
| 分层实时表+轻量视图 | 秒级 | 优 | 低 |
综合来看,PostgreSQL 利用物化视图做预计算,是实现近实时聚合的可行路径。只要合理设计刷新频率、建立唯一索引并配合分层结构,就能在报表与监控大屏中兼顾性能与时效性。
PostgreSQL物化视图近实时聚合修改时间:2026-08-02 13:18:31