postgresql物化视图需手动刷新才生效,首次创建后必须立即执行refresh;mysql无原生物化视图,应使用带唯一约束的增量汇总表;慢join优先补驱动表和被驱动表的关联字段索引。

物化视图在 PostgreSQL 里怎么建才真正生效
PostgreSQL 原生不支持物化视图自动刷新,MATERIALIZED VIEW 创建后必须手动触发 REFRESH MATERIALIZED VIEW,否则查到的永远是创建那一刻的快照。很多人建完就以为“自动更新”了,结果线上数据越来越旧。
- 首次创建后立即执行
REFRESH MATERIALIZED VIEW my_mv,否则查询返回空或陈旧数据 - 如果依赖表有写入,必须在业务低峰期或事务外刷新,否则会锁住源表(
CONCURRENTLY可缓解,但要求有唯一索引) - 没有主键或唯一列时,
REFRESH MATERIALIZED VIEW CONCURRENTLY直接报错:ERROR: could not create unique index - 物化视图不继承源表的索引,记得单独在
my_mv上建CREATE INDEX ON my_mv (user_id)
MySQL 没有物化视图,替代方案选临时表还是汇总表
MySQL 8.0 仍无原生物化视图,强行用 CREATE TEMPORARY TABLE 不靠谱——连接断开就消失;真正能落地的是带生命周期管理的汇总表(summary_orders_by_day),靠定时任务驱动。
- 别用
TEMPORARY TABLE做 JOIN 加速:它只对当前会话可见,应用多实例时完全不可控 - 汇总表字段必须和常用 JOIN 条件、WHERE 字段对齐,例如频繁按
order_date和shop_id关联,就该有(order_date, shop_id, total_amount)组合 - 用
INSERT ... SELECT全量重刷成本高,优先用增量更新:INSERT INTO summary ... ON DUPLICATE KEY UPDATE,前提是汇总表有UNIQUE(order_date, shop_id) - 注意时区:如果业务跨时区,
order_date存 UTC 还是本地时间?JOIN 时和业务表不一致会导致关联失败
JOIN 太慢,但又不能改表结构,先加什么索引最见效
90% 的慢 JOIN 不是缺物化视图,而是缺驱动表上的连接字段索引。优化顺序一定是:先看 EXPLAIN 输出里的 type 是不是 ALL 或 index,再补索引。
- 被驱动表(
JOIN右侧)的关联字段必须有索引,否则走嵌套循环就是全表扫描,比如SELECT * FROM orders o JOIN users u ON o.user_id = u.id,users.id必须是主键或有索引 - 驱动表(左侧)如果有
WHERE条件,它的过滤字段+关联字段组合索引效果最好,例如WHERE status = 'paid' AND created_at > '2024-01-01',就建INDEX(status, created_at, user_id) - 联合索引顺序不能错:等值条件放前,范围条件放后,
user_id在created_at后面就用不上 - 避免隐式类型转换:如果
orders.user_id是INT,而users.id是VARCHAR,即使都有索引,JOIN 也会失效
预计算结果存 Redis 还是数据库,关键看更新频率和一致性要求
高频读、低频写的关联结果(比如首页商品分类销量 Top10),Redis 是更轻量的选择;但只要涉及强一致性(如财务对账类 JOIN),哪怕慢点也得走数据库。
- Redis 适合存“可容忍分钟级延迟”的结果,用
HSET存字段,EXPIRE控制过期,更新时用DEL + HSET避免脏数据 - 如果预计算逻辑含
SUM/COUNT且源表每秒写入上百次,Redis 里做实时累加容易丢数,不如数据库里用INSERT ... ON DUPLICATE KEY UPDATE原子更新 - 别把整个 JOIN 结果集塞进一个 Redis key:超大 value 会阻塞主线程,拆成按维度分片,比如
sales:category:101、sales:category:102 - 应用层缓存失效逻辑要和数据库更新在同一个事务里完成,否则必然出现“刚更新完数据库,缓存还是旧的”
物化或预计算不是银弹——它把计算压力从查询时挪到了写入时或定时任务里。真正难的是判断“哪部分数据值得预计算”,这得看慢查询日志里反复出现的 JOIN 模式,而不是一上来就建视图。










