从业DBA多年,始终致力于OLTP的实时交易类型的数据库。龙年伊始,接手了阿联酋惠普 deliver给阿曼电信的名为Dragon的系统,该系统是用于反恐的data warehousing和BI分析系统,这才第一次和数据仓库有了接触,区区两个月的时间,也没有什么特别深的体会,只是针对工作中碰到的基于大数据量分区表的物化视图有了更多的理解,在此小结一下。
DBA都知道,物化视图是oracle数据库中较低级的一种数据同步方式,针对早期的8i版本的complete refresh,我以前开过一次trace,发现底层居然采用先delete后insert的低级方式来同步数据。自此,我对物化视图这项技术有些不屑一顾,甚至唯恐避之而不及。但是针对报表统计的分析系统,物化视图通过提前生成实际的物理数据,可以为报表统计提供更加快捷的查询效率,如果能够配合高效的快速刷新模式,它还是一种非常好的解决方案。
阿曼电信的BI系统数据库,核心业务数据是通话详单、短信详单、GPRS详单等数据,由于以上几类话单的数据量非
常大,因此数据需要利用range分区,按照每天一个分区存储在分区表中,这是物化视图的基表,数据在每天都需要通过ETL加载一次。而报表统计需要的数据,是通过物化视图手动增量刷新生成的,同样,在每天的ETL加载完基表之后,也需要对物化视图进行一次增量刷新。在技术层面上来说,需要创建基于分区表的物化视图,每天手动进行一次物化视图的增量刷新,而增量刷新的数据其实就是一个分区的数据。
针对这种情况,我采用了PCT(Partition Change Tracking)快速刷新的物化视图,该项技术从9i版本开始引入,也是本篇文章介绍的第一个物化视图技术。
一、PCT(Partition Change Tracking)快速增量刷新物化视图
在进行PCT快速增量刷新时,建立的是分区级别的刷新粒度,而不是和整个表建立联系判断哪些记录需要增量刷新,因此缩小了增量刷新的判断范围,同时增加了增量刷新的效率。而且在使用PCT快速增量刷新时,不需要物化视图日志,因此也就减少了维护物化视图日志所带来的额外开销。
为了能够支持PCT快速增量刷新,需要满足以下几个主要条件:
1. 物化视图的基表必须至少有一个是分区表;
2. 分区表必须是range或list分区;
3. 分区键值(Partition Key)必须是只由一个字段组成;
4. 物化视图的SELECT列表中需要包括分区键值字段,如果使用了GROUP BY,则在GROUP BY的列表中也需要包括分区键值字段。
针对阿曼电信的情况, 以上前3个条件都满足了,唯独第4个不行,由于基表是按天进行分区,而物化视图需要使用GROUP BY统计多天的数据,因此无法在物化视图的SELECT和GROUP BY中使用分区键值。在这种情况下,oracle提供了另外一种方式,即分区标记(Partition Marker)来代替分区键值,分区标记是通过DBMS_MVIEW.PMARKER函数来生成的。详细的使用方法如下例所示,其中的红色字体表示分区标记:
CREATE MATERIALIZED VIEW OMANTELBI.MV_T51_GPRS_CELLID_DAYWS REFRESH FAST ON DEMAND WITH ROWID AS select DBMS_MVIEW.PMARKER(rowid), trunc(START_DATE_TIME) as "Date", SERVED_CELL_ID as Cell_ID, sum(DURATION) as Duration_Aggre, sum(DATA_VOLUME_UP_LINK) as Upload_Aggre, sum(DATA_VOLUME_DOWN_LINK) as Download_Aggre from OMANTELBI.T01_GSM_GPRS_EVENT group by DBMS_MVIEW.PMARKER(rowid), trunc(START_DATE_TIME), SERVED_CELL_ID;
以上方法是通过DBMS_MVIEW.PMARKER标记唯一的rowid来帮助物化视图实现PCT快速增量刷新的案例。实际测试表明,PCT快速增量刷新比普通的快速增量刷新的效率更高,而且避免了物化视图日志的额外维护,使得对分区基表进行的ETL数据加载过程也受益匪浅。
由于阿曼电信的这套BI系统还处在试运行阶段,因此对于已经通过ETL加载并且已经刷新到物化视图的数据,常常需要删除掉一天的数据,然后再重新加载和刷新。针对这种情况,我采用了对物化视图进行分区的方法来提高维护的效率,这也是本篇文章介绍的第二种物化视图技术。
二、对物化视图做分区
对物化视图进行分区后,如果需要针对特定的分区进行重新刷新的操作,则可以对分区物化视图执行truncate partition操作,再利用PCT快速增量刷新只刷新truncate掉的分区,其他分区的数据则保持不变,大大提高了对物化视图维护的效率。
对物化视图进行分区,需要利用到物化视图的prebuilt table特性,即先按照分区语法创建物化视图的prebuilt 表,再利用该prebuilt表创建物化视图,详细语法如下例所示:
CREATE TABLE part_sales_tab_mv(time_id, cust_id, sum_dollar_sales, sum_unit_sale) PARALLEL PARTITION BY RANGE (time_id) (PARTITION month1 VALUES LESS THAN (TO_DATE('31-12-2010', 'DD-MM-YYYY')) PCTFREE 0 PCTUSED 99 STORAGE (INITIAL 64k NEXT 16k PCTINCREASE 0) TABLESPACE sf1, PARTITION month2 VALUES LESS THAN (TO_DATE('31-12-2011', 'DD-MM-YYYY')) PCTFREE 0 PCTUSED 99 STORAGE (INITIAL 64k NEXT 16k PCTINCREASE 0) TABLESPACE sf2, PARTITION month3 VALUES LESS THAN (TO_DATE('31-12-2012', 'DD-MM-YYYY')) PCTFREE 0 PCTUSED 99 STORAGE (INITIAL 64k NEXT 16k PCTINCREASE 0) TABLESPACE sf3) AS SELECT s.time_id, s.cust_id, SUM(s.amount_sold) AS sum_dollar_sales, SUM(s.quantity_sold) AS sum_unit_sales FROM sales s GROUP BY s.time_id, s.cust_id;
从语法上看,上面的语句与创建一个分区表的语句没有任何区别,只须留意一下红色字体的表的名称,玄机在下面这个创建物化视图的语句上,物化视图的名称和上面的prebuilt 表的名称必须相同,并且要注意增加ON PREBUILT TABLE关键字:
CREATE MATERIALIZED VIEW part_sales_tab_mv ON PREBUILT TABLE ENABLE QUERY REWRITE AS SELECT s.time_id, s.cust_id, SUM(s.amount_sold) AS sum_dollar_sales, SUM(s.quantity_sold) AS sum_unit_sales FROM sales s GROUP BY s.time_id, s.cust_id;
以上是对物化视图进行分区的操作案例。我曾试图用指定特定分区的方法进行分区增量刷新,但发现DBMS_MVIEW.REFRESH根本就没有指定分区的参数,因此略感遗憾。我想今后的新版本或许能增加这个功能,使得对分区物化视图的维护更加高效和便捷。
通过对物化视图分区时用到的prebuilt table这个功能,我又无心插柳的搞定了另外一个问题,prebuilt不仅能帮助我们创建分区物化视图,还可以帮助我们对物化视图增加字段或修改字段类型,也是本篇文章介绍的第三个物化视图技术。
三、如何快速修改物化视图的字段
还是这个BI项目,报表开发team的同事提出,在物化视图已经存在大量数据的情况下,想给物化视图增加一个字段,并且把另外一个字段的类型从varchar2修改成number,又不想重新全量刷新物化视图,有什么方法可以实现?
我查了查SQL手册,根本就没有ALTER MATERIALIZED VIEW … MODIFY和ALTER MATERIALIZED VIEW … ADD这样的语句,阿曼电信的数据量很大,drop掉物化视图,再重新创建并且全量刷新会用很长时间,就没有更好的办法了吗?
这时prebuilt table就派上用场了,针对已经存在大量数据的物化视图,修改字段的操作居然可以在几秒以内完成!
在这里需要再强调一下segment的概念,在数据库逻辑存储的概念上讲,物化视图和表一样,都是segment。然而不同的是,我们不能像对表增加字段那样直接给物化视图增加字段。但是我们可以利用它们在存储层相同的特质,利用prebuilt table的功能,采用变通的方法来给物化视图直接增加字段,下面的例子将详细解释。
首先创建一个基表T1:
1 | Create table T1 (id char (2), amount number); |
创建prebuilt table,注意表名MV1和打算后续创建的物化视图名称相同:
1 | Create table MV1 (id char (2), sum number); |
创建物化视图日志:
1 | Create materialized view log on T1 with row id including new values ; |
创建物化视图使用on prebuilt table功能:
1 | Create materialized view mv1 on prebuilt table refresh fast |
2 | As select id, sum (amount) sum from t1 group by id; |
这时物化视图已成功创建,接下来删掉MV1这个物化视图。
1 | Drop materialized view MV1; |
由于我们使用了prebuilt table,因此MV1这个物化视图被drop了之后,在数据字典里MV1这个表也就是MV1这个segment依然存在。这时我们可以使用alter table语句来修改字段类型,或者增加字段:
1 | Alter table T1 add ( name varchar2(20)); |
这时再执行创建物化视图的语句:
1 | Create materialized view mv1 on prebuilt table refresh fast |
2 | As select id, name , sum (amount) sum from t1 group by id, name ; |
MV1这个物化视图又被成功创建了,不但成功添加了name字段,而且还包含删除之前所有的数据,因为这个segment根本就是一直存在的。
Prebuilt table实在是个好工具,建议创建物化视图时都使用这种方法,针对大数据量的情况下尤其应该如此。
希望借此文章,能让大家在碰到大数据量的物化视图方案时,能够展开紧锁的眉头,可以有更多的选择。