数据仓库中的事实表是用于存储业务事实数据的核心组件,是数据仓库架构中的关键部分。
事实表基础
事实表特性
事实表作为数据仓库维度建模的核心,紧紧围绕着业务过程来设计,通过获取描述业务过程的度量来表达业务过程,包含了引用的维度和业务过程有关的度量。
事实表中一条记录所表达的业务细节程度被称为粒度(业务中的细节程度)。通常粒度可以通过两种方式来表达:一种是维度属性组合所表示的细节程度,另一种是所表示的具体业务含义。
作为度量业务过程的事实,一般为整型或浮点型的十进制数值,有可加性、半可加性和不可加性三种类型:
- 可加性事实: 是指可以按照与事实表关联的任意维度进行汇总
- 半可加性事实: 只能按照特定维度汇总,不能对所有维度汇总,比如库存可以按照地点和商品进行汇总,而按时间维度把一年中每个月的库存累加起来则毫无意义
- 不可加事实: 不具备可加性,比如比率型事实。对于不可加性事实可分解为可加的组件来实现聚集
相对维表来说,通常事实表要细长的多,行的增加速度也比维表快很多。
维度属性也可以存储到事实表中,这种存储到事实表中的维度列被称为“退化维度”。与其他存储在维表红的维度一样,退化维度也可以用来进行事实表的过滤查询、实现聚合操作等。
事实表有三种类型:
- 事务事实表: 用来描述业务过程,跟踪空间或时间上某点的度量事件,保存的是最原子的数据,也称为“原子事实表”
- 周期快照事实表: 具有规律性的、可预见的时间间隔记录事实,时间间隔如每天、每月、每年等
- 累积快照事实表: 用来表述过程开始和结束之间的关键步骤事件,覆盖过程的整个生命周期,通常具有多个日期字段来记录关键时间点,当过程随着生命周期不断变化时,记录也会随着过程的变化而被修改
事实表设计原则
- 原则1:尽可能包含所有与业务过程相关的事实
事实表设计的目的是为了度量业务过程,所以分析哪些事实与业务过程有关是设计中非常重要的关注点。在事实表中应该尽量包含所有与业务过程相关的事实,即使存在冗余,但是因为事实通常为数字型,带来的存储开销也不会很大。
- 原则2:只选择与业务过程相关的事实
在选择事实时,应该注意只选择与业务过程有关的事实。比如在订单的下单这个业务过程的事实表设计中,不应该存在支付金额这个表示支付业务过程的事实。
- 原则3:分解不可加性事实为可加的组件
对于不具备可加性条件的事实,需要分解为可加的组件。比如订单的优惠率,应该分解为订单原价金额与订单优惠金额两个事实存储在事实表中。
- 原则4:在选择维度和事实之前必须先声明粒度
粒度的声明是事实表设计中不可忽视的重要一步,粒度用于确定事实表中一行所表示业务的细节层次,决定了维度模型的扩展性,在选择维度和事实之前必须先声明粒度,且每个维度和事实必须与所定义的粒度保持一致。在设计事实表的过程中,粒度定义得越细越好,建议从最低级别的原子粒度开始,因为原子粒度提供了最大限度的灵活性,可以支持无法预期的各种细节层次的用户需求。在事实表中,通常通过业务描述来表述粒度。
- 原则5:在同一个事实表中不能有多种不同粒度的事实
事实表中的所有事实需要与表定义的粒度保持一致,在同一个事实表中不能有多种不同粒度的事实。
下图所示为机票支付成功事务事实表,粒度为票级别。
在实际业务中,一个订单可以同时支付多张机票,如 ID 为 100901 的订单包含三张机票,ID 为 100902 的订单包含两张机票,ID 为 100903 的订单包含一张机票。在该事实表的设计中,机票支付金额和机票折扣金额两个事实与表定义的粒度一致,并且支持按表的任意维度汇总,可以添加进该事实表中。而订单支付金额和订单票数作为上一层粒度的订单级事实,与该票级事实表的粒度不一致,且不能进行汇总。比如订单 ID 为 100901 的订单支付金额为 3700 元,订单票数为 3 张,如果这两个度量在该表进行汇总计算总订单金额和总票数,则会造成重复计算的问题,所以不能作为该表的度量选入。
- 原则6:事实的单位要保持一致
对于同一个事实表中事实的单位,应该保持一致。比如原订单金额、订单优惠金额、订单运费金额这三个事实,应该采用一致的计量单位,统一为元或分,以方便使用。
- 原则7:对事实的 NULL 值要处理
对于事实表中事实度量为 NULL 值的处理,因为在数据库中 NULL 值对常用数字型字段的 SQL 过滤条件都不生效,比如大于、小于、等于、不等于,建议用零值填充。
- 原则8:使用退化维度提高事实表的易用性
在 Kimball 的维度建模中,通常按照星型模型的方法来设计,对于维度的获取采用的是通过事实表的外键关联专门的维表的方式,谨慎使用退化维度。而在大数据领域的事实表设计中,则大量采用退化维度的方式,在事实表中存储各种类型的常用维度信息。这样设计的目的主要是为了减少下游用户使用时关联多个表的操作,直接通过退化维度实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等。通过增加冗余存储的方式减少计算开销,提高使用效率。
事实表设计方法(★)
在 Kimball 的 《数据仓库工具箱》 一书中,对于维度模型设计采用四步设计方法:
- 选择业务过程
- 声明粒度
- 确定维度
- 确定事实
在当前的互联网大数据环境下,面对复杂的业务场景,为了更有效、准确地进行维度模型建设,基于 Kimball 的四步维度建模方法,进行了更进一步的改进。
- 第一步:选择业务过程及确定事实表类型
在明确了业务需求以后,接下来需要进行详细的需求分析,对业务的整个生命周期进行分析,明确关键的业务步骤,从而选择与需求有关的业务过程。
以淘宝的正向订单流转为例,如下图所示。
业务过程通常使用行为动词表示业务执行的活动,比如上图中的淘宝订单流转的业务过程有四个:创建订单、买家付款、卖家发货、买家确认收货。在明确了流程所包含的业务过程后,需要根据具体的业务需求来选择与维度建模有关的业务过程。比如是选择买家付款这个业务过程,还是选择创建订单和买家付款这两个业务过程,具体根据业务情况来确定。
在选择了业务过程以后,相应的事实表类型也随之确定了。比如选择买家付款这个业务过程,那么事实表应为只包含买家付款这一个业务过程的单事务事实表;如果选择的是所有四个业务过程,并且需要分析各个业务过程之间的时间间隔,那么所建立的事实表应为包含了所有四个业务过程的累积快照事实表。
- 第二步:声明粒度
粒度的声明是事实表建模非常重要的一步,意味着精确定义事实表的每一行所表示的业务含义,粒度传递的是与事实表度量有关的细节层次。明确的粒度能确保对事实表中行的意思的理解不会产生混淆,保证所有的事实按照同样的细节层次记录。
应该尽量选择最细级别的原子粒度,以确保事实表的应用具有最大的灵活性。同时对于订单过程而言,粒度可以被定义为最细的订单级别。比如在淘宝订单中有父子订单的概念,即一个子订单对应一种商品,如果拍下了多种商品,则每种商品对应一个子订单;这些子订单一同结算的话,则会生成一个父订单。那么在这个例子中,事实表的粒度应该选择为子订单级别。
- 第三步:确定维度
完成粒度声明以后,就意味着确定了主键,对应的维度组合以及相关的维度字段就可以确定了,应该选择能够描述清楚业务过程所处的环境的维度信息。比如在淘宝订单付款事务事实表中,粒度为子订单,相关的维度有买家、卖家、商品、收货人信息、业务类型、订单时间等维度。
- 第四步:确定事实
事实可以通过回答“过程的度量是什么”来确定。应该选择与业务过程有关的所有事实,且事实的粒度要与所声明的事实表的粒度一致。事实有可加性、半可加性、非可加性三种类型,需要将不可加性事实分解为可加的组件。
比如在淘宝订单付款事务事实表中,同粒度的事实有子订单分摊的支付金额、邮费、优惠金额等。
- 第五步:退化维度
在传统的维度建模的星型模型中,对维度的处理是需要单独存放在专门的维表中的,通过事实表的外键获取维度。这样做的目的是为了减少事实表的维度冗余,从而减少存储消耗。而在大数据的事实表模型设计中,考虑更多的是提高下游用户的使用效率,降低数据获取的复杂性,减少关联的表数量。所以通常事实表中会冗余方便下游用户使用的常用维度,以实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等操作
事务事实表(类型一)
订单作为交易行为的核心载体,直观反映了交易的状况。订单的流转会产生很多业务过程,而下单、支付和成功完结三个业务过程是整个订单的关键节点。获取这三个业务过程的笔数、金额以及转化率是日常数据统计分析的重点,事务事实表设计可以很好地满足这个需求。本节将介绍三种不同事务事实表的设计方法,以及在淘宝交易订单中关于邮费和折扣分摊到子订单的算法。
设计过程
任何类型的事件都可以被理解为一种事务。比如交易过程中的创建订单、买家付款,物流过程中的揽货、发货、签收,退款中的申请退款、申请小二介入等,都可以被理解为一种事务。事务事实表,即针对这些过程构建的一类事实表,用以跟踪定义业务过程的个体行为,提供丰富的分析能力,作为数据仓库原子的明细数据。下面以淘宝交易事务事实表为例,阐述事务事实表的一般设计过程。
- 选择业务过程
上图中给出了淘宝交易订单的流转过程,其中介绍了四个重要过程:创建订单、买家付款、卖家发货、买家确认收货,即下单、支付、发货和成功完结四个业务过程。这四个业务过程不仅是交易过程中的重要时间节点,而且也是下游统计分析的重点,因此淘宝交易事务事实表设计着重从这四个业务过程进行展开。
Kimball 维度建模理论认为,为了便于进行独立的分析研究,应该为每个业务过程建立一个事实表。对于是否将不同业务过程放到同一个事实表中,将在下一节中详细介绍。
- 确定粒度
业务过程选定以后,就要针对每个业务过程确定一个粒度,即确定事务事实表每一行所表达的细节层次。下面先介绍淘宝订单的产生过程。
淘宝出售商品主要分两类卖家:一类是个人性质的闲置卖家,主要出售闲置的或者二手商品;一类是拥有店铺的卖家,以出售新商品为主。接下来主要以店铺类交易订单为例进行介绍。在淘宝下单交易时,有两种方式:一种是选定商品后直接购买,这样会产生一个交易订单;一种是将多种商品加入到购物车中,然后一起结算,此时对于每一种商品都会产生一个订单,同时对于同一个店铺会额外产生一个订单,即父订单;由于是在同一个店铺购买的,所以父订单会承载订单物流、店铺优惠等信息。而对于每一种商品产生的订单就称为子订单,子订单记录了父订单的订单号,并且有子订单标志。如果在同一个店铺只购买了一种商品,则会将父子订单进行合并,只保留一条订单记录。如下图所示。
了解了淘宝交易订单的产生过程后,现在为淘宝交易事务事实表确定粒度。如第 1 步(选择业务过程)所述,在淘宝交易过程中有四个重要业务过程,需要为每个业务过程确定一个粒度。其中下单、支付和成功完结三个业务过程选择交易子订单粒度,即每个子订单为事务事实表的一行,每个子订单所表达的细节信息诶:交易时间、卖家、买家、商品,即选择图(父子订单合并成一条记录)中订单 ID 为 1、9 和图(父子订单拆开成多条记录)中订单 ID 为 4、5、6、7、8 的子订单作为事务事实表的每一行。卖家发货这个业务过程可以选择子订单粒度,即将每个子订单作为卖家发货事实表的一个细节。然而,在实际操作中发现,卖家发货更多的是物流单粒度而非子订单粒度,同一个子订单可以拆开成多个物流单进行发货。在事务事实表设计过程中,秉承确定为最细粒度的原则,因此对于卖家发货确定为物流单粒度,和其他三个业务过程不同,这样可以更好地给下游统计分析带来灵活性。
- 确定维度
选定好业务过程并且确定粒度后,就可以确定维度信息了。在淘宝交易事务事实表设计过程中,按照经常用于统计分析的场景,确定维度包含:买家、卖家、商品、商品类目、发货地区、收货地区、父订单维度以及杂项维度。由于订单的属性较多,比如订单的业务类型、是否无线交易、订单的 attributes 属性等,对于这些使用较多却又无法归属到上述买卖家或商品维度中的属性,则新建一个杂项维度进行存放。如下图所示。
- 确定事实
作为过程度量的核心,事实表应该包含与其描述过程有关的所有事实。以淘宝交易事务事实表为例,选定三个业务过程——下单、支付和成功完结,不同的业务过程拥有不同的事实。比如在下单业务过程中,需要包含下单金额、下单数量、下单分摊金额;在支付业务过程中,包含支付金额、分摊邮费、折扣金额、红包金额、积分金额;在完结业务过程中包含确认收货金额等。由于粒度是子订单,所以对于一些父订单上的金额需要分摊到子订单上,比如父订单邮费、父订单折扣等。具体的分摊算法将在“父子事实的处理方式”一节中介绍。
根据 Kimball 维度建模理论,经过以上四步,淘宝交易事务事实表已成型,可以满足下游分析统计的需要。然而,阿里巴巴数据仓库在建模时,基于以上四步增加了一步——退化维度,这个过程在 Kimball 维度建模中也有所提及;但阿里巴巴数据仓库出于效率和资源的考虑,将常用维度全部退化到事实表中,使下游分析使用模型更加方便。
- 冗余维度
在确定维度时,包含了买卖家维度、商品维度、类目维度、收发货维度等,Kimball 维度建模理论建议在事实表中只保存这些维表的外键,而淘宝交易事务事实表在 Kimball 维度建模基础之上做了进一步的优化,将买卖家星级、标签、店铺名称、商品类型、商品特征、商品属性、类目层级等维度属性都冗余到了事实表中,提高对事实表进行过滤查询、统计聚合的效率。如下图所示。
经过以上五个步骤,完成了淘宝交易事务事实表的设计。
但在设计过程中遗留一个问题,即对于单一事实表中是否包含多个业务过程,还没有给出定论。接下来将通过在淘宝和 1688 交易过程中采用不同的设计方案来阐述两种设计方法。
单事务事实表
单事务事实表,顾名思义,即针对每个业务过程设计一个事实表。这样设计的优点不言而喻,可以方便地对每个业务过程进行独立的分析研究。1688 交易流程则采用这种模式构建事务事实表。
1688 交易和淘宝交易相似,主要流程也是下单、支付、发货和完结,而在这四个关键流程中 1688 交易选择下单和支付两个业务过程设计事务事实表,分布是 1688 交易订单下单事务事实表和 1688 交易订单支付事务事实表。
选定业务过程后,将对每个业务过程确定粒度、维度和事实。对于 1688 交易订单下单事务事实表,确定子订单粒度、选择买家、卖家、商品、父订单、收货地区维度,事实包含下单分摊金额和折扣金额,如下图所示。
而对于 1688 交易订单支付事务事实表,粒度和维度与交易订单下单事务事实表相同,所表达的事实则不一样,包含支付金额、支付调整金额和支付优惠等,如下图所示。
1688 交易针对下单和支付分别建立单事务事实表后,每天的下单记录则进入当天的下单事务事实表中,每天的支付记录进入当天的支付事务事实表中,由于事实表具有稀疏特质,因此只有当天数据才会进入当天的事实表中。下面以具体交易订单为例,展示单事务事实表的设计实例。
如下图所示,订单 order1 在 2016-01-01 下单并且在当天完成支付;
多事务事实表
两种事实表对比
- 1. 业务过程
对于单事务事实表,一个业务过程建立一个事实表,只反映一个业务过程的事实;对于多事务事实表,在同一个事实表中反映多个业务过程。多个业务过程是否放到同一个事实表中,首先需要分析不同业务过程之间的相似性和业务源系统。比如淘宝交易的下单、支付和成功完结这三个业务过程是存在相似性的,都属于订单处理中的一环,并且都来自于交易系统,因此适合放到同一个事务事实表中。
- 2. 粒度和维度
在考虑是采用单事务事实表还是多事务事实表时,另一个关键点就是粒度和维度,在确定好业务过程后,需要基于不同的业务过程确定粒度和维度,当不同业务过程的粒度相同,同时拥有相似的维度时,此时就可以考虑采用多事务事实表。如果粒度不同,则必定是不同的事实表。比如交易中支付和发货有不同的粒度,则无法将发货业务过程放到淘宝交易事务事实表中。
- 3. 事实
对于不同的业务过程,事实往往是不同的,单事务事实表在处理事实上比较方便和灵活,仅仅体现同一个业务过程的事实即可;而多事务事实表由于有多个业务过程,所以有更多的事实需要处理。如果单一业务过程的事实较多,同时不同业务过程的事实又不相同,则可以考虑使用单事务事实表,处理更加清晰;若使用多事务事实表,则会导致事实表零值或空值字段较多。
- 4. 下游业务使用
单事务事实表对于下游用户而言更容易理解,关注哪个业务过程就使用相应的事务事实表;而多事务事实表包含多个业务过程,用户使用时往往较为困惑。1688 和淘宝交易分别采用了这两种方式,从日常使用来看,对于淘宝交易事务事实表下游用户确实有一定的学习成本。
- 5. 计算存储成本
针对多个业务过程设计事务事实表,是采用单事务事实表还是多事务事实表,对于数据仓库的计算存储成本也是参考点之一,当业务过程数据来源于同一个业务系统,具有相同的粒度和维度,且维度较多而事实相对不多时,此时可以考虑使用多事务事实表,不仅其加工计算成本较低,同时在存储上也相对节省,是一种较优的处理方式。
两种事务事实表的比较如下表所示。
父子事实表的处理方式
事实的设计准则
周期快照事实表(类型二)
特性
实例
注意事项
累积快照事实表(类型三)
设计过程
特点
特殊处理
物理实现
三种事实表的比较
无事实的事实表
聚集型事实表
聚集的基本原则
聚集的基本步骤
阿里公共汇总层
聚集补充说明
参考资料
大数据之路-阿里巴巴大数据实践
原创文章,转载请注明出处:http://www.opcoder.cn/article/97/