最可靠方式是查pg_matviews的last_refresh_time并与源表最新更新时间比对;last_refresh_time为null表示从未刷新,需结合updated_at或pg_stat_all_tables等判断源表是否变更,避免依赖pg_stat_progress_refresh_matview等临时状态。

直接查 pg_matviews 的 last_refresh_time,再和源表最新更新时间比对——这是最可靠、开销最小的判断方式。
查物化视图最后刷新时间
PostgreSQL 把物化视图元信息存在系统视图 pg_matviews 里,其中 last_refresh_time 字段记录了最后一次成功刷新的时刻。它不是估算值,是真实写入的时间戳。
- 执行
SELECT matviewname, last_refresh_time FROM pg_matviews WHERE matviewname = 'your_mv_name'; - 如果
last_refresh_time为NULL,说明该物化视图从未被刷新过(即使创建时用了WITH DATA) - 注意:这个字段只在成功完成
REFRESH后才更新;失败或中断不会写入
对比源表的最新变更时间
仅看刷新时间不够,还得知道源表“有没有动过”。PostgreSQL 不自动追踪表更新时间,所以得自己加一层保障:
- 如果源表有
updated_at或modified_time这类业务时间戳字段,用SELECT MAX(updated_at) FROM source_table;获取最新值,和last_refresh_time对比 - 如果没有业务时间戳,可退而求其次查
pg_stat_all_tables的last_data_change(需开启track_commit_timestamp = on并在事务中使用SET LOCAL track_commit_timestamp = on) - 更粗粒度但稳定的方式:检查源表的
n_tup_upd + n_tup_del是否增长(来自pg_stat_all_tables),结合上次刷新后的统计快照做差值判断
为什么不能只依赖 pg_stat_progress_refresh_matview
这个视图只在刷新进行中才显示活跃记录,且生命周期极短——刷新一结束就清空。它无法回答“是否需要刷新”,只能告诉你“此刻正在刷”。误把它当状态指标会导致两种典型错误:
- 查询返回空结果,就以为“不需要刷”——其实可能刚刷完,也可能根本没刷过
- 看到某条记录,就以为“还没刷完”,进而重复触发刷新,引发锁冲突或资源争抢
- 它不暴露刷新是否成功,也不保存历史,纯属运行时临时状态
容易被忽略的边界点
真正上线后最容易翻车的地方,往往藏在细节里:
-
last_refresh_time是基于服务器本地时区的timestamp with time zone,如果应用层用的是 UTC 时间做调度,直接比大小会出错 - 并发刷新(
CONCURRENTLY)失败时,last_refresh_time不更新,但物化视图数据可能已部分损坏(比如唯一索引冲突导致中间状态残留) - 如果物化视图定义里用了
NOW()或CURRENT_DATE这类不稳定函数,每次刷新结果都不同,此时“是否需要刷新”本身就成了伪命题——得靠业务逻辑兜底,而不是依赖时间比对










