跳转至

物化视图

核心问题:物化视图解决了什么问题?它和普通视图有什么区别?


它解决了什么问题?

复杂的聚合查询(如月度销售报表)每次执行都需要扫描大量数据,耗时数秒甚至数分钟。物化视图将查询结果持久化存储,查询时直接读取预计算结果,速度极快。

生活类比:普通视图是"每次问都现场计算",物化视图是"提前算好存起来,问的时候直接给答案"。


普通视图 vs 物化视图

对比项 普通视图 物化视图
数据存储 不存储,每次查询执行原始 SQL 存储在磁盘,查询极快
数据新鲜度 实时 需要手动或定时刷新
查询性能 取决于原始 SQL 复杂度 极快(直接读取预计算结果)
适用场景 简化查询,数据实时性要求高 复杂聚合,数据实时性要求低
可建索引 ✅ 可以在物化视图上建索引

创建与使用

-- 创建物化视图(存储查询结果)
CREATE MATERIALIZED VIEW monthly_sales AS
SELECT 
    DATE_TRUNC('month', order_date) AS month,
    SUM(amount) AS total_sales,
    COUNT(*) AS order_count
FROM orders
GROUP BY DATE_TRUNC('month', order_date);

-- ✅ 在物化视图上建索引,进一步加速查询
CREATE INDEX ON monthly_sales (month);

-- 查询物化视图(极快,直接读取预计算结果)
SELECT * FROM monthly_sales WHERE month = '2024-01-01';

刷新物化视图

-- 普通刷新(锁表,刷新期间不可查询)
REFRESH MATERIALIZED VIEW monthly_sales;

-- ✅ 并发刷新(不锁表,刷新期间仍可查询旧数据)
-- 前提:物化视图上必须有唯一索引
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales;

为什么并发刷新需要唯一索引:并发刷新时,PostgreSQL 需要对比新旧数据,找出变化的行进行增量更新,唯一索引用于标识每一行。


定时刷新(结合 pg_cron)

-- 使用 pg_cron 扩展定时刷新(每天凌晨2点刷新)
SELECT cron.schedule(
    'refresh-monthly-sales',
    '0 2 * * *',
    'REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales'
);

适用场景

场景 说明
报表统计 月度/季度销售报表,每天刷新一次即可
数据大屏 实时性要求不高(5分钟级别),但查询要快
复杂聚合 多表 JOIN + GROUP BY,原始查询耗时数秒
搜索优化 预计算搜索结果,加速搜索接口

工作中的坑

错误 原因 解决方案
刷新时业务查询被阻塞 使用了普通 REFRESH(锁表) 改用 REFRESH CONCURRENTLY(需要唯一索引)
并发刷新报错 物化视图上没有唯一索引 先建唯一索引再并发刷新
数据不一致 忘记刷新物化视图 配置定时任务自动刷新

面试高频问题

Q:物化视图和普通视图有什么区别?什么时候用物化视图?

普通视图不存储数据,每次查询都执行原始 SQL,数据实时;物化视图将查询结果持久化存储,查询极快,但需要手动或定时刷新。当查询复杂(多表聚合)、数据实时性要求不高(允许分钟级延迟)时,用物化视图。

Q:物化视图刷新时如何避免影响业务查询?

使用 REFRESH MATERIALIZED VIEW CONCURRENTLY(并发刷新),刷新期间仍可查询旧数据,不锁表。前提是物化视图上必须有唯一索引。