普通视图每次查询都重新执行底层SQL,不存数据,导致性能瓶颈;物化视图预存结果但需手动刷新,SQL Server索引视图需SCHEMABINDING和NOEXPAND提示才生效。

普通视图每次查询都重新执行底层 SQL
“视图慢”不是语法问题,而是它根本没存数据——每次 SELECT * FROM my_view,数据库都会把视图定义里的整个 SELECT 语句原样展开、重新解析、生成执行计划、再跑一遍。如果视图里有 JOIN 三张大表 + GROUP BY + 窗口函数,那每次查都在重复做这些高开销操作。
常见错误现象:EXPLAIN 显示扫描行数远超预期;同一查询直接跑等价 SQL 比查视图快好几倍;并发稍高,CPU 就飙到 90%+。
- 嵌套视图(A → B → C)会让展开后的语句极长,优化器容易选错连接顺序或放弃下推
WHERE -
ORDER BY或LIMIT在视图定义里,可能阻止外层条件的下推,导致先排序全量再过滤 - 标量函数、UDF 出现在视图中,基本等于强制逐行调用,无法并行也无法索引加速
物化视图是预计算结果的物理副本
物化视图(如 PostgreSQL 的 MATERIALIZED VIEW)真正在磁盘上存了一份查询快照,相当于建了一张只读表。查它不走原始逻辑,而是直接从本地存储读数据——没有解析开销、没有实时 JOIN、也不依赖基表索引是否匹配。
但关键代价是:这份数据不是自动更新的。你建完 CREATE MATERIALIZED VIEW mv_sales AS SELECT ...,之后基表哪怕改了一行,mv_sales 里还是旧的,除非手动执行 REFRESH MATERIALIZED VIEW mv_sales。
-
REFRESH CONCURRENTLY允许边查边刷,但要求物化视图必须有唯一索引(比如主键或UNIQUE NOT NULL字段),否则报错 - 刷新是全量重算(即使只改了 1 行),增量刷新需额外设计(如用触发器打标记 + 增量 SQL),PostgreSQL 原生不支持
- 物化视图本身可建索引(
CREATE INDEX ON mv_sales (status)),这对后续查询提速很关键——很多人建完就忘加索引,结果还是全表扫
SQL Server 的“索引视图”是个特例
只有 SQL Server 支持真正意义上的“带聚集索引的视图”,即 CREATE VIEW ... WITH SCHEMABINDING 后,再建 UNIQUE CLUSTERED INDEX。它不像物化视图那样占额外空间,但要求极其严格:
- 视图必须用
SCHEMABINDING创建,且所有引用的表和列都不能被删或改名 - 聚集索引键必须是
UNIQUE、NOT NULL、确定性表达式(不能含GETDATE()、NEWID()等) - 查询必须满足“引用提示”(
WITH (NOEXPAND))才会走索引,否则优化器可能忽略它,继续走底层表
MySQL 和 SQLite 完全没有原生物化视图支持,只能用 CREATE TABLE AS SELECT + 定时任务模拟,但无法保证刷新原子性,也难处理并发写入冲突。
选哪个,取决于你能不能接受延迟
如果业务要求“用户下单后 1 秒内报表里就得体现”,那物化视图或索引视图都不适合——普通视图是唯一选择,优化重点只能放在底层表索引、精简字段、拆分复杂逻辑上。
但如果场景是日报、BI 看板、后台运营分析,80% 查询都集中在固定维度聚合(比如“各城市昨日销售额”),那物化视图带来的性能提升往往是数量级的。这时候真正容易被忽略的不是“怎么建”,而是“怎么刷”:刷新时机、锁表影响、失败重试机制、以及是否忘了给物化表本身建索引。










