2008年10月29日星期三

利用开源技术建立数据仓库

利用开源技术建立数据仓库

简介

本文的主要内容是建立数据仓库。数据仓库是一种计算机数据库系统,用于收集、整合和存储组织的数据,帮助生成准确的、时间性的管理信息,以及支持数据分析。本文解释了一个好的数据仓库的重要性,并涵盖了利用开源技术建立一个数据仓库的流程。

本文面向的读者

本文主要面向软件开发者、数据库管理员、软件集成商,或者其它有兴趣面临大数据量业务数据分析挑战的读者。阅读本文的读者需要具备数据库、信息系统和业务事务的基本只是。

为什么要建立数据仓库

有很多理由说明建立数据仓库的重要性,但是大部分理由都可以归纳为一个基本的愿望:提供一种数据分析的方式,来支持管理决策。你可能已经为管理者提供过很 多数据,例如:信息系统网站的使用统计,引用趋势,以及注册用户的数量。这些都是基本的信息,可以直接从信息系统获得。然而,这些数据存在很大的缺陷。

首先,直接从信息系统提取数据无疑会增加系统的负载。而数据分析过程通常需要进行大规模地数据处理。另外,处理过程中可能会发生其它一些问题,例如,在数据提取过程中系统数据库的表被锁定。而数据仓库与信息系统是完全分离的,甚至可以运行于不同的系统。

其次,一个OLTP(联 机事务处理)系统的数据模型是针对数据分析专门优化的。以前,我们都是根据我们的实体关系,来开发使用正规化数据库模型的系统。这对于信息系统来说,是合 适的,因为基础模型能够正确反映系统的情况。但是,这种模型使得查询大数据量聚合数据变得非常困难。而且,这种数据库设计的冗余能力很差,因为难以维护, 经常造成数据不一致,甚至更糟。而对于数据分析,数据冗余非常有用,它会提高处理效率。

此外,OLTP(联机事务处理)系统中的数据可以随时间变化。例如,在一个在线的客户关系管理系统中,客户迁徙到其它国家,在这种情况下,可以采用多种方式进行数据处理:

  • 更新客户的记录,消除其先前的国家信息和分析信息;
  • 在应用系统中创建新的记录,用于记录这个客户信息。

这两种方式都不太令人满意,因为它们丢失了客户的历史信息。而数据仓库能够解决这个问题,在数据仓库中,可以保存客户在所有时期内的状态记录,从中很容易得到起变化情况。

最后,可能存在很多信息系统不用的数据源,这些数据源可以用于数据分析。数据仓库可以为所有这些数据提供集中的存储,收集到的信息可以在一步内完成查询。

创建数据仓库

在本章中,我将指导你使用不同的方式来创建数据仓库。我将用一个基于web的简单信息系统来描述如何建立一个数据仓库。这个基于web的信息系统包括请求、用户和页面之类的信息实体。这个信息系统的数据模型暂缺

设计一个纬度化的数据仓库

提出问题

创建数据仓库最重要的步骤是设计。你可能会嘀咕:“管理层希望知道什么?”。首先,你不得不通过分析数据的属性,找出需要回答的问题。例如,用户和 页面存在关联吗?是否存在某类的用户只访问某些页面,不访问其它的页面?访问者从哪些页面来,到哪些页面去?我相信通过这种方式,至少在80%的问题上, 管理层会给出满意的答案。

既然你已经读到了这里,我就假定你公司管理层已经回答了类似的问题。如果没有,就让他们做。不要小看这个阶段,它是数据仓库成败的基础。

结构模型化

一旦你通过一些问题获得了管理层的关注点,下一步就是要根据这些关注点决定如何安排数据仓库中的数据。例如,销售人员的数据应该在多种不同的结构中进行操作,而不应该只局限于职位。在OLAP世界中,这些结构叫做数据立方体(Cube?),因为它是传统二维数据库的扩展。根据数据的不同,一个数据立方体可以有两个以上的纬度。我们将在关系数据库中,利用星形模式为数据立方体(Cube?)建模,利用一个fact(好多人翻译为事实?)表,将一系列的纬度表连接起来。

选择事实元素的粒度(fact grain)

创建数据立方体(Cube?) 的关键是为其找一个好的事实表(fact table)。这一步的操作很简单,在事实表(fact table)中,每一个“事实(fact)”元素各占一行,因此难点变成了如何添加有用的纬度。好的事实表的标准是,数据的粒度尽可能细。数据立方体经常 是单个事务,例如支付,在我们的例子中,是页面请求。

添加纬度

现在我们有了一个数据立方体的中心起始点,现在是时间加入新的纬度来修饰基本事实元素,纬度表保存事实元素的属性。如果你想不出事实表的任何纬度, 可能是因为起始的粒度选择存在问题。在所有的纬度中,时间纬度是最为显而易见的。在时间轴上,每个请求都有一个位置,因此我们建立一个纬度表"time" 来保存所有的请求实体,这个表的每个记录代表每个记录发生的时间。如果两个请求同时发生,在时间纬度表中它们被连接到同一个实体。

我们还需要添加一个代表页面的纬度。每个条目都是一个页面信息,对应事实表中的一个请求。

最后,我们还需要添加一个纬度,表示发起请求的用户。事实表中的每个请求,都是由已知用户,或者根本不是用户发起的,所有这些都报存在用户纬度表。

在我们创建并连接纬度表时,需要遵循一个重要的规则,不要使用在线系统的现有键,因为我们根本无法对其进行任何控制,它们或许会改变,甚至消失。作 为替代,每个纬度都需要一个自己的代理键,这个键称为“技术键(technical key)”,在整个数据仓库中都是唯一的。

构建数据仓库

现在我们知道,数据立方体会被表达为星形模式,下面我们将开始构建工作。在这一步,我们将使用来自于OLTP系统的数据填充一个数据仓库。这个阶段的流程叫做ETL?,即提取、转换、加载。这是我们所需要做的,从各种数据源中提取事实表和纬度表所需的数据,转换为我们所需要的形式,加载到数据仓库,以备查询、分析。我们将使用KETTLE执行这些操作,KETTLE是一个开源的ETL?工具。KETTLE有两个软件包组成。Spoon和Pan用于创建和执行转换(transformation)操作,而Chef和Kitchen用于从转换(transformation)中定义作业并调度和执行它们。

使用Spoon设计转换

现在,我们开始使用Spoon创建一个用于填充数据仓库的转换(transformation)。为了填写事实表,到所有纬度的键必须是事先知道的。因此,我们需要明确区分两类纬度:

  1. 纬度的组成数据事先已经明确位于在线的信息系统。
  2. 纬度的组成数据需要从事实数据和周边数据源中生成。

在第二种情况下,我们是在填写事实表的过程中,生成或更新纬度数据的,因此可以同时得到其键值,但在第一种情况下,我们做不到。

在我们的例子中,时间和页面纬度属于第二种情况,就是说他们是在更新事实表的时候生成的。然而,由于用户纬度基于信息系统的表,需要提前获取其键 值。因此,我们将通过两个独立的转换(transformation)来填写数据仓库,第一个针对数据已经存在的用户纬度,而第二个针对其它纬度和事实 表。

一些准备工作

在使用Spoon之前,我们需要先定义数据源。在这个例子中,我们只有一个数据源:信息系统的数据库。我们需要将这个数据库作为一个连接(Connection)添加到Spoon。我们的目的数据库连接也可以在此定义。

更新第一种纬度

在我们的例子中,用户纬度是唯一一个不能在填写纬度表的过程中更新的纬度。用户数据已经被保存在源信息系统的一个表中,所以我们需要使用Spoon的“Dimension lookup/update”功能将数据读出、处理。

这一步用于创建、更新和查询变化缓慢的纬度。数据源中的数据还会发生变更,例如用户信息会发生变化,处理数据更新有以下两种方式:

  1. 更新现有记录;
  2. 创建另外纬度记录,维护全部的过程记录。

如果使用第二种方式,就需要在纬度表中加入数据变更的识别信息,例如:“version”, “date_from” and “date_to”等。

在我们的例子中,我们需要按照第二种方式来处理用户纬度,所以Spoon中的转换(transformation)如图3所示。首先,从信息系统用户表中读出数据;接着,按照纬度表的要求使用"select"指令对其进行过滤;最后,将过滤后的数据插入数据仓库的user_dim表中。在Spoon的文档中有详细的说明,所以我们接下来只介绍如何配置纬度查询/更新(dimension lookup/update)

由于我们是直接从信息系统的表中创建用户纬度表,所以信息系统用户表中的主键是多余的。在示例信息系统中,使用用户的email作为主键。而在数据 仓库中,我们使用一个唯一的技术主键代替原来的主键。后续的流程能够取得这个技术主键,用于纬度表和事实表的关联。在我们的例子中,这个技术主键字段叫做user_id

在信息系统用户表的字段中,我们需要指定对于纬度比较重要的字段。在我们的例子中,我们需要持续跟踪用户全名、所属公司和性别等信息。

对于第二种纬度,版本字段用于跟踪缓慢变更纬度的不同版本。而日期范围用于表示每个版本存在的期限。如果信息系统能够跟踪记录的变更,这有助于使用Stream Datefield进行更好的数据校验,不过这已经超出本文的范围。通常,默认的已经足够了。



更新第二种纬度表和事实表

现在,第一种纬度已经准备就绪,现在需要做得是填写事实表。在这个流程中,需要更新剩下的纬度表,在需要时,使其技术建准备就绪。

我们以从请求日志中,为事实表提取基本粒度信息,作为工作的起点。

接着,使用Spoon的JavaScript step提取请求日志的时间戳字段,生成日期纬度,代码见附录B:使用JavaScript生成日期纬度。使用Dimension Lookup/Update,我们将这些数据放入数据库,然后使用技术键time_id进行引用。

接下来就是用户纬度。记住,我们在上一步已经对用户纬度进行过更新,所有现在我们应该将其与事实表中的每个请求实体进行关联。通过对用户登录行为与会话的 查询,我们可以将会话与用户实现对应,进而实现用户与请求行为之间的对应。如果会话没有实现与用户的匹配,其键值(email)就设置为NULL,它匹配 一种特殊的情况:未知用户(the unknown user)。由于过滤操作是多线程操作的,因此我们使用Spoon的排序步骤(sort step)对用户信息数据流进行排序。当数据进入“Lookup user_dim“,这股数据流包含一个额外的字段叫做"email",这个字段包含每个请求的email,如果未知则为NULL。记住,我们指定email作为查询主键,所以使用这个字段,转换(transformation)步骤能够为数据流中的每个条目找到一个技术键,最后加入一个额外的字段user_id,就构成了用户纬度表的条目内容。

页面纬度表所需的数据已经保存在请求日志中了,填写这个纬度表的都是层次化的数据,包括域名、路径、页面,最后生成一个技术键字段,就构成了一个页面纬度表的条目。

我们另外加了一个访问来源(Referrer)纬度,这个纬度我们上面没有提到过,与页面纬度相对应。

最后,将填写事实表的数据过滤出来,只包括纬度的技术键和事实元素。将他们插入表request_facts中。

聚合数据

解释如何创建聚合表来加速分析操作。

将所有工作合到一起

通过上面的工作,我们完成了转换(transformation)设计,下面需要使用Kettle的Chef和Kitchen将这三个转换(transformation)顺序结合成一个作业,并在流程中加入日志和调试功能。

工具Chef无需多做解释,因为他的界面与Spoon是一样的。只不过,Spoon是将步骤(step)结合成转换(transformation),而 Chef将转换(transformation)组合成作业。由于Chef的文档很好,所以这里我们只用一个截屏来解释。

在我们的案例中,有两类纬度,因此将数据仓库的填写流程分为两个转换(transformation)阶段。首先,Chef执行更新用户纬度的转换 (transformation)。如果转换失败,它就发送一个包含细节错误信息的邮件;如果转换成功,就进入第二个阶段,更新其他纬度和事实表。在这个 例子中,我希望如果全部工作成功完成也收到一封邮件,因此将最后一步转换连接到Mail

最后需要说明,Chef可以对作业进行调度。利用命令行工具Kitchen,使Kettle成为一个强大的系统,用于创建、更新和维护数据仓库。

使用数据仓库

本章将讲述如何对数据仓库进行分析。

准备在线分析处理

结构模型化,我们从采用星形模式建立一个数据立方体开始,数据立方体包含一个事实表将各个纬度表关联到一起。在数据立方体内,每个纬度都能够与其他纬度实现多对多的关联。

这种特殊的数据组织形式,使通过一种广义的表单方式进行数据查询成为可能。这就是OLAP服务器的任务。在我们的案例中,我们将使用开源的关系形OLAP服务器Mondrian完成这个工作。

从关系化到纬度化

上面解释过,数据仓库中的数据采用关系的方式存储。事实表是多个纬度的连接点,每个纬度都有自己的表和数据。在一个OLAP数据立方体中,数据以多维的方式进行组织。

每个数据立方体都包含多个指标(measure),这些指标可以从事实表中得到,用于计算聚合和分组数据。一个数据立方体的指标可以包括诸如:销售额、产 品价格或者这些事实元素的综合等。在非事实化的事实表中,可以没有任何指标。每个数据立方体都有多个纬度。一个纬度是一个查看数据的方向,由关系数据库的 一个一维表构成。最显而易见的纬度当然就是时间了。

每个纬度都可以有一个活多个层次。正如你所期待的那样,通过层次可以纬度的信息进行逐次聚合。例如,在时间纬度中,可以使用天-月-季度-年作为层次。在数据分析过程中,层次对于数据集合和分组是很有用的。

上述概念可以被OLAP服务器用于多维数据查询。下面我将告诉你如何配置Mondrian OLAP服务器来从事这个工作。

使用Mondrian做数据分析

Mondrian OLAP服务器的安装在此就不赘述了。安装完成之后,我们需要告诉它数据仓库的结构。这是由一种XML格式的文档实现的,文档包含OLAP数据立方体信 息,及其纬度、层次和指标信息,以及如何从关系数据库中生成这些信息。在我们的案例中,Mondrian的相关XML模式见附录C:Mondrian XML schema示例。其中,只有一个数据立方体,叫做"Requests",这个立方体直接关联到表“request_facts”,有一个指标和四个纬度。

在事实表中只有一个事实元素,因此我们无需过多关注,而指标也只有一个:每个请求id的数量,当然它永远等于一。

在数据立方体中,我们定义了四个纬度,用户(User)、页面(Page)、访问来源(Referrer),以及时间(Time)。每个纬度都有一个或多个层次。对于每个纬度,Mondrian需要知道是由那个表表示的,以及哪个键是事实表的外链键。

一旦Mondrian知道如何解释数据仓库的关系数据库,它就可以作为一个转换层,将多维数据查询转换到关系数据库。

执行多维查询

Mondrian利用OLAP数据立方体的纬度属性,向数据仓库提出尽可能复杂的问题。为了完成这项工作,Mondrian使用一种叫做MDX的语言,实现将多维变量到SQL转换。其语法如下所示:

SELECT [

[, ...]]
FROM []
[WHERE []]

MDX与SQL不同,SQL查询只能范围二维数据集:一个表,包含固定的列数、多行记录,而MDX查询是针对多维数据集,返回的结果也可以有任何数量的纬度。例如,下面的一个查询可以查询在某些月中,哪些公司访问国我们的信息系统。

SELECT 	{[Time].[Months].Members} ON COLUMNS,

{[Users].[Company Name].Members} ON ROWS
FROM [Requests]
WHERE [Time].[2006]

可视化和展示

最后,最终用户需要通过一个系统看到多维查询得到的各种结果,这可以通过JPivot实现。

JPivot已经包含到Mondrian的发布版本中了,无需多做设置。它的配置也非常简单:只要指定JSP文件使用的数据集,启动查询即可。再进一步, 大家可以考虑Pentaho。很多工程如:JPivot和JFreereports都已经集成到了Pentaho中,更加简化了生成展示界面的工作。

附录A:技术简介

在这一章中,我将简单介绍一些数据仓库创建过程中涉及到的一些关键技术,以及相关工程的连接。

Kettle

http://www.kettle.be

Kettle是一个开源的ETL套件,能够从多种不同的数据源中提取数据,转换后加载到数据仓库。

Kettle包含部分工具:

  • Spoon和Pan用于创建和执行数据转换。
  • Chef和Kitchen用于将多个转换组合为作业,并调度和执行这些作业。

Mondrian

http://mondrian.sourceforge.net

Mondrian是一个关系型OLAP服务器,作为关系数据库之上的一层,执行多维数据查询操作。。

Jpivot

http://jpivot.sourceforge.net

JPivot是一个基于Mondrian的表示层工具。它生成多维查询指令,并将结果以交互式的数据透视表(pivot table)展示出来。

附录B:使用JavaScript生成日期纬度

A dimension present in almost all data warehouses is that of time. Spoon's JavaScript? step makes it easy to extract hierarchical data from date or time fields in the source system. While most of this is described in Spoon's documentation, an example of how to do this is given here. JavaScript code

// The fields we want to calculate

var day_of_month;
var week_of_year;
var month_of_year;
var year;
var quarter;
var name_day;
var name_month;

// Calculate!
day_of_month = dateTime.Clone().dat2str("dd");
week_of_year = dateTime.Clone().Clone().dat2str("ww");
month_of_year = dateTime.Clone().dat2str("MM");
year = dateTime.Clone().dat2str("yyyy");

name_day = dateTime.Clone().dat2str("E").getString();
name_month = dateTime.Clone().dat2str("MMMM").getString();

if(month_of_year <= 3) {
quarter = "Q1";
}
else if(month_of_year <= 6) {
quarter = "Q2";
}
else if(month_of_year <= 9) {
quarter = "Q3";
}
else {
quarter = "Q4";
}

附录C:Mondrian XML schema示例



name="DataWareHouseTest">
name="Requests">
name="request_facts"/>
name="Users"foreignKey="user_id">
hasAll="true"allMemberName="All Users"primaryKey="user_id">
name="user_dim"/>
name="Company Branche"column="companybranche"uniqueMembers="false"/>
name="Company Name"column="companyname"uniqueMembers="false"/>
name="Role"column="role"uniqueMembers="false"/>

hasAll="true"primaryKey="user_id">
name="Sex"column="sex"uniqueMembers="false"/>
name="User"column="email"uniqueMembers="true">
name="First Name"column="firstname"/>
name="Infix"column="infix"/>
name="Last Name"column="lastname"/>



name="Page"foreignKey="page_id">
hasAll="true"primaryKey="page_id">
name="page_dim"/>
name="Domain"column="domain"/>
name="Path"column="path"/>
name="Page"column="page"/>


name="Referrer"foreignKey="referrer_id">
hasAll="true"primaryKey="referrer_id">
name="referrer_dim"/>
name="Domain"column="referrerDomain"/>
name="Path"column="referrerPath"/>


name="Time"type="TimeDimension"foreignKey="time_id">
hasAll="false"primaryKey="time_id">
name="time_dim"/>
name="Year"column="year"type="Numeric"uniqueMembers="true"
levelType="TimeYears"/>
name="Quarter"column="quarter"uniqueMembers="false"
levelType="TimeQuarters"/>
name="Month"column="month_of_year"uniqueMembers="false"type="Numeric"
levelType="TimeMonths"/>

hasAll="true"name="Weekly"primaryKey="time_id"
defaultMember="[Time.Weekly].[All Time.Weeklys]">
name="time_dim"/>
name="Year"column="year"type="Numeric"uniqueMembers="true"
levelType="TimeYears"/>
name="Week"column="week_of_year"type="Numeric"uniqueMembers="false"
levelType="TimeWeeks"/>
name="Day"column="day_of_month"uniqueMembers="false"type="Numeric"
levelType="TimeDays"/>


name="Pages"column="id"aggregator="count"datatype="Integer"formatString="#,##"/>


参考

  • A Dimensional Modeling Manifesto – Ralph Kimball 1997
  • Slowly Changing Dimensions – Ralph Kimball 1996
  • Spoon Documentation – Pentaho 2001-2006

版本介绍

v146 - 1st of September 2006 – Initial release

The first version to be released to the community, still in draft form. I would like to thank my employer, Better be, for giving me the time and resources to work on this document. Without their support, this document would not exist. Also, I would like to thank, in advance, anyone who will contribute to this document in the future. You make open source to what it is today.

- Michel Jansen

v197 - 6th of October 2006

A revision with a lot of the sharp edges removed. 1.Fixed the calculation of quarters in the JavaScript? example – Thanks Paul Keenan 2.Corrected the “Asking questions” section – Thanks Ronald van der Blink

- Michel Jansen

2008年10月16日星期四

虚拟化方案的不完全对比

厂商 qumranet Citrix Micro$oft Vmware SUN
名称 SolidICE、kvm XenSource Hyper-V Vmware xVM(原virtualbox)
操作系统 Linux Linux Windows2008 Windows、Linux Windows、Linux
授权方式 开源 KVM内核模块(GPL),已经是Linux的标准虚拟运算组件。 xen Hypervisor,曾经是Linux标准虚拟运算组件的有力竞争者,但是败给kvm,前景不明朗,即使有citrix撑腰。 目前还存在一个virtualbox的开源分支,叫做virtualbox-ose,开发相当活跃。
免费


Vmware ESX SUN xVM
商业 SolidICE XenSource Hyper-V 其它产品
虚拟模式 半虚拟化 测试阶段 支持 支持 支持 支持
全虚拟化 不支持 不支持
支持(是全虚拟化的鼻祖) 支持
硬件虚拟化 支持 支持 支持 支持 支持
客户机OS Linux 支持 支持 支持 支持 支持
Windows 支持 支持 支持 支持 支持
其它 支持 支持 支持 支持 支持
远程接入协议 SPICE(VNC的基础上进行了修改)、RDP、XDMCP ICA(专有) RDP(专有,与ICA同源) RDP