PostgreSQL 14至16版本均不支持物化视图自动刷新,必须显式调用REFRESH MATERIALIZED VIEW;推荐用pg_cron插件定时刷新,或通过LISTEN/NOTIFY加外部监听器实现事件驱动刷新。

PostgreSQL 14+ 原生不支持自动刷新的物化视图
直接说结论:PostgreSQL 直到 16 版本,MATERIALIZED VIEW 仍**没有内置的自动刷新机制**(比如定时、触发式或 ON COMMIT 刷新)。你执行 CREATE MATERIALIZED VIEW 后,它就是一张静态快照,必须显式调用 REFRESH MATERIALIZED VIEW 才能更新。所谓“自动”,只能靠外部调度或触发逻辑模拟。
用 pg_cron 插件实现定时刷新(推荐生产方案)
pg_cron 是最常用、最可靠的定时刷新方案,它把 cron 调度集成进 PostgreSQL 进程内,无需依赖操作系统 crontab,且事务安全。
实操步骤:
- 确保 PostgreSQL 已启用
pg_cron:在postgresql.conf中添加shared_preload_libraries = 'pg_cron',重启服务 - 连接数据库后运行:
CREATE EXTENSION IF NOT EXISTS pg_cron; - 授予调度权限(通常给 owner):
GRANT USAGE ON SCHEMA cron TO your_user; - 提交定时任务,例如每 5 分钟刷新一次:
SELECT cron.schedule('refresh_mv_stats', '*/5 * * * *', 'REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_summary;');
注意:CONCURRENTLY 是关键——它允许刷新时不阻塞对物化视图的读取,但要求该视图**已有唯一索引**(通常是主键或含唯一约束的列组合),否则会报错 ERROR: cannot refresh materialized view "mv_user_summary" concurrently, because it has no unique index。
用 LISTEN/NOTIFY + 外部监听器做事件驱动刷新
如果你希望“数据一变就刷新”,而不是固定周期,可结合业务写入逻辑主动通知。PostgreSQL 本身不监听表变更,但你可以让应用在 INSERT/UPDATE 后发通知。
典型流程:
- 在目标基表上创建触发器(可选,或由应用层保证):
CREATE OR REPLACE FUNCTION notify_mv_refresh() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('mv_refresh_queue', TG_TABLE_NAME); RETURN NEW; END; $$ LANGUAGE plpgsql; - 绑定触发器到关键表:
CREATE TRIGGER tr_user_changed AFTER INSERT OR UPDATE ON users EXECUTE FUNCTION notify_mv_refresh(); - 用 Python/Node.js 等写一个常驻监听进程,调用
LISTEN mv_refresh_queue,收到消息后执行REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_summary
这个方式延迟低,但增加了运维复杂度;而且要注意避免刷新风暴——比如批量导入时可能触发数百次通知,建议加简单去重或合并策略(如延迟 1 秒后刷新,期间新通知覆盖旧计时)。
为什么不能用触发器直接在数据库内刷新?
很多人第一反应是:“在基表上建 AFTER INSERT 触发器,里面调 REFRESH MATERIALIZED VIEW 不就行了?”——这会失败,因为 REFRESH MATERIALIZED VIEW 是**DDL 操作,不能在触发器函数中执行**(报错:ERROR: REFRESH MATERIALIZED VIEW cannot be executed from a function)。
即使改用 dblink 异步调用,也容易引发死锁、事务隔离问题或连接泄漏。官方明确不推荐这类 hack 方式。真正可控的路径只有两种:定时(pg_cron)或外部事件监听。
别忽略并发刷新冲突:多个定时任务或通知同时触发 REFRESH CONCURRENTLY 不会报错,但后启动的会等前一个完成;而普通 REFRESH(无 CONCURRENTLY)会直接锁住整个 MV,阻塞所有查询。线上务必用 CONCURRENTLY + 唯一索引。

















