1. 数据仓库和物化视图的关系
对于典型的数据仓库来说,基于每月、每周、每天的大量数据流从一个或多个在线的OLTP系统传入数据仓库系统。其大小往往有几百G或几个T,而这些巨大的主要数据则是被存储于几个非常大的fact表。然而这些数据在被导入数据仓库数据库 之前通常被放置到staging文件里。
在数据仓库里常用于提高性能的技术 是 创建汇总(summaries)。汇总是采用提前计算非常消耗时间和资源的join和汇总并把结果存在表里以提高查询性能的特殊视图,这就是所谓的物化视 图。物化视图除了可以提高查询性能外还可以用于复制数据(在一个分布式或移动计算的环境里,可以利用物化视图把数据从远程服务器或中央服务器下载到本地, 然后通过同步或周期性的刷新机制来更新数据)。
物化视图对于应用层是透明的,不需要有任何的改动,终端用户甚至都感觉不到底层是用的物化视图。其实对于物化视图来说最重要的就是两个特性:刷新(refresh)和查询重写(query rewrite),后面我会专题介绍这两个特性。
2. 物化视图的管理 概述
使用物化视图的目的就是提高性能,但是和物化视图相关联的管理维护任务也是个大问题。对于物化视图的管理维护需要考虑以下问题:
表明初始化要建什么类型的物化视图
物化视图索引
确保每次数据库更新的时候都能适当的刷新物化视图和物化视图索引
检查哪些物化视图被使用了
判断物化视图在负载性能提升上的效果
检测被物化视图使用的空间
判断要新创建一个什么类型的物化视图来提高性能
判断哪个存在的物化视图需被删除
归档没用的细节数据和物化视图数据
3. 物化视图的类型
Materialized Views with Aggregates
Materialized Views Containing Only Joins
Nested Materialized Views
3.1 Materialized Views with Aggregates
在数据仓库里,物化视图通常都包含集聚函数。如果要实现快速刷新,SELECT子句列里必须包括所以的GROUP BY 里出现的列(如果有group by的话),并且还必须要有COUNT(*), COUNT(column)。 同时,必须在定义物化视图里所有被引用的表上定义了物化视图日志。
支持的集聚函数有:SUM, COUNT(X), COUNT(*), AVG, VARIANCE, STDDEV, MIN, MAX 和有集聚函数组成的表达式。
对包含joins和aggregates的物化视图进行任何类型的DML都有可能实现快速刷新功能(direct load or conventional INSERT, UPDATE, DELETE)。刷新有两种选项ON COMMIT和ON DEMAND,不同的刷新方式对性能的影响也是不同的,因此应根据具体的情况来选择刷新方法。
下面举些例子来帮助理解:
例一:
创建物化视图product_sales_mv计算每个个产品总的销量和总销售额。指定build immediate关键字表明在创建时就产生数据,并可用于查询重写。由于创建了物化视图日志因此可以指定刷新方式为FAST。
CREATE MATERIALIZED VIEW LOG ON products WITH SEQUENCE, ROWID
(prod_id, prod_name, prod_desc, prod_subcategory, prod_subcategory_desc,
prod_category, prod_category_desc, prod_weight_class, prod_unit_of_measure,
prod_pack_size, supplier_id, prod_status, prod_list_price, prod_min_price)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON sales
WITH SEQUENCE, ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW product_sales_mv
PCTFREE 0 TABLESPACE demo
STORAGE (INITIAL 8k NEXT 8k PCTINCREASE 0)
BUILD IMMEDIATE
REFRESH FAST
ENABLE QUERY REWRITE
AS SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales,
COUNT(*) AS cnt, COUNT(s.amount_sold) AS cnt_amt
FROM sales s, products p
WHERE s.prod_id = p.prod_id GROUP BY p.prod_name;
例二:
创建物化视图product_sales_mv_02,只按prod_name计算总销售额。该物化视图初始化的时候并不包含数据,由于指定了BUILD 方法是BUILD DEFERRED。对于build deferred的物化视图必须执行一次完全刷新才能实现快速刷新功能,刷新完产生数据后就可以使用查询重写功能了。对于数据量比较大,在创建MV时系统 处于高运行状态,比较适合这种方法而不影响系统性能。
CREATE MATERIALIZED VIEW product_sales_mv
PCTFREE 0 TABLESPACE demo
STORAGE (INITIAL 16k NEXT 16k PCTINCREASE 0)
BUILD DEFERRED
REFRESH COMPLETE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales
FROM sales s, products p WHERE s.prod_id = p.prod_id
GROUP BY p.prod_name;
例三:
在单个表sales上创建物化视图。由于在例一中在sales被引用的所有列上创建了物化视图日志,因此可以实现快速刷新功能。由于指定了刷新方式是 refresh fast on commit,所以对sales表的DML操作在commit后都将刷新物化视图。
CREATE MATERIALIZED VIEW LOG ON sales WITH SEQUENCE, ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW sum_sales
PARALLEL
BUILD IMMEDIATE
REFRESH FAST ON COMMIT AS
SELECT s.prod_id, s.time_id, COUNT(*) AS count_grp,
SUM(s.amount_sold) AS sum_dollar_sales,
COUNT(s.amount_sold) AS count_dollar_sales,
SUM(s.quantity_sold) AS sum_quantity_sales,
COUNT(s.quantity_sold) AS count_quantity_sales
FROM sales s
GROUP BY s.prod_id, s.time_id;
因此,对于创建包括aggregate函数的物化视图具有一下规则:
3.2 MV Containing Only Joins
有一些物化视图仅包含join并没有集聚函数aggregates,这类物化视图的好处是提前执行非常消耗资源和时间的joins。
对只包含join的物化视图的基表进行任何类型的DML(direct-path, conventional INSERT, UPDATE, DELETE)都可以实现快速刷新。
只包含join的物化视图可以定义为refresh ON COMMIT 或 ON DEMAND。
如果指定REFRESH FAST,那么oracle 将校验查询语句已保证在细节表被更新后能相应的执行快速刷新。这些额外检查是:
1). 每个细节表都必须创建了物化视图日志,除非这个表支持PCT。同时,ROWID列也必须在物化视图日志。
2). 所有细节表的rowids必须出现在物化视图的查询语句的SELECT 列表。
如果有些限制条件不满足,可以指定REFRESH FORCE来最大可能话快速刷新特性。
例一:
CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON times WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON customers WITH ROWID;
CREATE MATERIALIZED VIEW detail_sales_mv
PARALLEL BUILD IMMEDIATE
REFRESH FAST AS
SELECT s.rowid "sales_rid", t.rowid "times_rid", c.rowid "customers_rid",
c.cust_id, c.cust_last_name, s.amount_sold, s.quantity_sold, s.time_id
FROM sales s, times t, customers c
WHERE s.cust_id = c.cust_id(+) AND s.time_id = t.time_id(+);
如果在例一中SELECT 列表中不包含times_rid, customers_rid,那么如果刷新方法为REFRESH FORCE,则这个物化视图只有当更新sales表时才具有快速刷新功能,而更新times 或customers表时并不具有更新功能。
3.3 嵌套的物化视图
嵌套的物化视图可以说是以上两种物化视图的组合,它是基于另一个物化视图来建立物化视图,当然在定义嵌套的物化视图时也可以引用物化视图之外的对象。
相对于前面两种物化视图来说,嵌套的物化视图能够提供更高效的性能。在典型的数据仓库系统里,创建物化视图时在单个join上包含许多aggregate 函数,这种形式的物化视图性能受很大的影响。而使用嵌套的物化视图,可以基于单表建立多个只包含join的物化视图,然后在这些物化视图的基础上再建立 aggregate物化视图,从而大大的提高性能。
例一:
创建只包含join的物化视图join_sales_cust_time,然后在该视图上建立物化视图日志,最后在该物化视图的基础上建包含aggregate的嵌套物化视图sum_sales_cust_time。
CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON customers WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON times WITH ROWID;
/*create materialized view join_sales_cust_time as fast refreshable at
COMMIT time */
CREATE MATERIALIZED VIEW join_sales_cust_time
REFRESH FAST ON COMMIT AS
SELECT c.cust_id, c.cust_last_name, s.amount_sold, t.time_id,
t.day_number_in_week, s.rowid srid, t.rowid trid, c.rowid crid
FROM sales s, customers c, times t
WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id;
/* create materialized view log on join_sales_cust_time */
CREATE MATERIALIZED VIEW LOG ON join_sales_cust_time
WITH ROWID (cust_last_name, day_number_in_week, amount_sold)
INCLUDING NEW VALUES;
/* create the single-table aggregate materialized view sum_sales_cust_time
on join_sales_cust_time as fast refreshable at COMMIT time */
CREATE MATERIALIZED VIEW sum_sales_cust_time
REFRESH FAST ON COMMIT AS
SELECT COUNT(*) cnt_all, SUM(amount_sold) sum_sales, COUNT(amount_sold)
cnt_sales, cust_last_name, day_number_in_week
FROM join_sales_cust_time
GROUP BY cust_last_name, day_number_in_week;