在Oracle数据库的日常使用中,视图(View)和物化视图(Materialized View)是两个经常被拿来比较的对象。不少刚接触Oracle的工程师以为两者只是性能上有差距,实际上它们从存储结构到工作机制都存在本质区别。理解这些差异,对做报表系统、数据仓库分层或者查询优化都有直接帮助。本文将从原理、性能、刷新机制和典型场景几个方面详细展开。

一、底层原理:一个存查询语句,一个存查询结果
普通视图本质上是一段被命名并存储在数据字典中的SQL语句,它不存放任何实际数据。当你查询一个视图时,Oracle会把视图的定义展开,与外层的查询条件合并,最终对基表执行查询。也就是说,视图只是一个逻辑上的窗口,访问视图和直接写等价的SQL访问基表,在执行层面几乎是一回事。
物化视图则完全不同,它会真实地把查询结果集物理存储在数据库中,拥有自己的段(Segment),占用实际的磁盘空间,可以像普通表一样建索引、做统计信息收集。查询物化视图时,Oracle直接读取已经算好的结果,不需要重新对基表做关联、聚合等开销大的运算。正因如此,物化视图常被称为“物理化”的查询结果。
从数据字典角度也能看出差异:普通视图的定义存放在USER_VIEWS的TEXT列中,而物化视图的信息记录在USER_MVIEWS和USER_MVIEW_LOGS里,并且可以在USER_SEGMENTS中查到对应的段信息。这种存储层面的差异,决定了两者在性能特征上的根本不同。
二、创建语法与查询性能对比
先看普通视图的创建,非常简单,就是一条带名字的SELECT语句:
-- 普通视图:只存储查询定义 CREATE OR REPLACE VIEW v_emp_dept AS SELECT e.employee_id, e.employee_name, d.department_name, d.location FROM employees e JOIN departments d ON e.dept_id = d.dept_id; -- 查询视图时,实际执行的是展开后的基表查询 SELECT * FROM v_emp_dept WHERE department_name = '研发部';
再看物化视图的创建,需要指定刷新方式和刷新时机:
-- 物化视图:存储实际查询结果 CREATE MATERIALIZED VIEW mv_sales_summary BUILD IMMEDIATE -- 创建时立即生成数据 REFRESH FAST ON COMMIT -- 基表提交时快速刷新 AS SELECT product_id, region, SUM(amount) AS total_amount FROM sales GROUP BY product_id, region; -- 可以为物化视图创建索引,进一步提升查询速度 CREATE INDEX idx_mv_sales_prod ON mv_sales_summary(product_id);
性能层面的差异可以概括为:普通视图的查询性能完全取决于基表查询本身,如果基表数据量大、关联复杂,视图查询照样慢;物化视图则是用空间换时间,查询时只扫描结果集,速度通常快几个数量级。但要注意,如果基表数据更新频繁,物化视图的刷新开销会反过来拖累写入性能,尤其是配置了ON COMMIT刷新的场景,每一次基表提交都会触发刷新逻辑,写入延迟会明显增加。
另外,Oracle的查询重写(Query Rewrite)功能是物化视图独有的优势。开启该功能后,即使业务SQL直接查的是基表,优化器也会自动判断能否改写为访问物化视图,从而在应用代码零改动的情况下获得性能提升。这一点普通视图无法做到。
三、数据刷新机制:物化视图绕不开的维护成本
普通视图由于不存数据,基表变了视图看到的内容自然就是最新的,不存在一致性问题。物化视图则必须面对“结果过期”的问题,这就引出了刷新机制。Oracle提供两种刷新方式:完全刷新(COMPLETE)会删除现有数据重新执行整个查询,实现简单但代价高;快速刷新(FAST)只把基表的增量变更应用到物化视图上,效率高但有前提条件,比如聚合查询必须满足特定的限制,还需要在基表上创建物化视图日志。
-- 在基表上创建物化视图日志,是快速刷新的前提
CREATE MATERIALIZED VIEW LOG ON sales
WITH PRIMARY KEY, ROWID
INCLUDING NEW VALUES;
-- 定时刷新:每天凌晨2点完全刷新一次
BEGIN
DBMS_REFRESH.MAKE(
name => 'mv_refresh_group',
list => 'mv_sales_summary',
next_date => SYSDATE,
interval => 'SYSDATE + 1'
);
END;
/刷新时机的选择也有讲究。ON COMMIT表示基表每次提交就刷新,数据实时性最好,但写入压力大;ON DEMAND则由人工或定时任务触发刷新,适合对实时性要求不高的统计场景。还可以配合REFRESH COMPLETE ON DEMAND配合调度任务,在业务低峰期统一刷新。判断一个物化视图是否还能快速刷新,可以用DBMS_MVIEW.EXPLAIN_MVIEW过程来分析。
一个常见的坑是:开发者以为建了物化视图日志就能快速刷新,结果刷新时报ORA-12008错误,多数原因是聚合条件、DISTINCT或外连接不满足快速刷新的语法限制。遇到这种情况,要么改写查询逻辑,要么退而求其次使用完全刷新。
四、使用场景选择:什么时候用哪个
普通视图的适用场景包括:需要简化复杂查询、为开发者屏蔽表结构细节、实现轻量级的权限控制(只暴露部分列给特定用户)、或者做逻辑分层但数据实时性要求高。由于它零存储成本、零维护成本,凡是查询本身不慢的场景,优先用普通视图就够了。
物化视图适合的场景则集中在查询重、变化少、可容忍延迟的地方。典型如数据仓库中跨多表的大规模聚合、报表系统中日累计或月累计统计、需要加速的复杂计算,以及分布式环境中需要定期同步远端数据的场景(配合REFRESH ... AS OF和数据库链路)。如果查询需要秒级响应,而底层聚合要跑几十分钟,物化视图往往是性价比最高的方案。
两者也可以组合使用:先用物化视图固化中间聚合结果,再在它上面建普通视图提供统一的对外查询口径,既保证了性能又保留了灵活性。总结一句话:普通视图是逻辑封装工具,物化视图是查询加速工具,按这个定位去选型,基本不会出错。