目录
• 概述
• 维度表设计
• 代理键
• 稳定维度
• 缓慢渐变维
• 拉链表详解
• 事实表设计
• 事实表设计
• 明细事实表
• 聚合事实表
1.概述
数据仓库维度建模主要分为维度表设计和事实表设计,下面分别进行学习。
2.维度表设计
2.1 代理键
维度表中必须有一个能够唯一标识一行记录的列(最好是原子性的列,不要是组合键), 通过该列维护维度表与事实表之间的关系,一般在维度表中业务主键符合条件可以当作维度 主键。
但是,数据仓库是整个公司数据的整合,这会涉及到多个数据源有相同维度,那么就会 出现以下两个问题:
• 当整合多个数据源的维度时,不同数据源的业务主键重复怎么办?
• 涉及维度拉链表时,同一主体多条记录,业务键重复怎么办?
如上图所示,业务键重复,我们可以引入代理键,如下表所示:
把多个系统的数据复合在一起,同时再维护一个代理键,而且代理键在这个维度表里是 唯一标识一条记录的,类似于业务系统的业务键。
代理键是由数据仓库处理过程中产生的、与业务本身无关的、唯一标识维度表中一条 记录并充当维度表主键的列,也是描述维度表与事实表关系的纽带。
在设计有代理键的维度表中,事实表中的关联键是代理键而不是原有的业务主键,即 业务关系是靠代理键维护,这样有效避免源系统变化对数仓数据对影响。
在实际业务中,代理键通常是数值型、自增的值。
2.2 稳定维度
部分维度表的维度是在维度表产生后,属性是稳定的、无变化的。比如时间维度、区域维度等,针对这种维度,设计维度表的时候,仅需要完整的数据,不需要天的快照数据, 因为当前数据状态就是历史数据状态。
2.3 缓慢渐变维
维度数据会随着时间发生变化,变化速度比较缓慢,这种维度数据通常称作缓慢渐变维,例如电商平台的用户维度表,用户可能会随着时间推移改变收件地址,因此用户维度表 中的收件地址就是一个缓慢变化维。由于数据仓库需要追溯历史变化,尤其是一些重要的数 据,所以历史状态也需要采取一定的措施进行保存,保存历史状态的方式有以下三种:
• 每天保存当前数据的全量快照数据(每天一个新增分区),该方案适合数据量较小 (根据公司具体的配置而定)的维度,使用简单的方式保存历史状态。
• 在维表中添加关键属性值的历史字段,仅保留上一个的状态值。可能同时有多个属 性都非常重要,而且只能追溯上一个数据,不是所有的历史数据,这种范式应用场景较少。
• 拉链表:当维度数据发生变化时,将旧数据置为失效,将更改后的数据当作新的 记录插入到维度表中,并开始生效,这样能够记录数据在某种粒度上的变化历史。
2.4 拉链表详解
将数据的变更当做流水记录下来 ,旧的设为失效,新的设为生效,如果粒度为天,那么就可以得到一天的最终状态作为最终状态。
表 5-4 中每条记录都有一个 End_date,当有新的数据产生时,在旧数据的 End_date 字 段中插入日期,然后新插入一条数据,新数据的 End_date 字段中是一个永久有效的值,如 果再发生更新,上一次更新数据的 End_date 字段设置为当前日期,然后再次插入新数据, 新数据的 End_date 字段中设置一个永久有效的值。
如果想知道某个员工在 5 月 22 号时在哪个部门,那么可以通过如下 SQL: Select * from user where start_date<= 2018-05-22 and end_date>= 2018-05-22
根据拉链表的结构,如果对维度表做拉链,那么一个维度实体必然存在多条记录,也 就是一个主键 ID 对应多条数据,此时维度表的原子性主键也就没有意义了。
维度表做拉链后会失去原子性主键,那么拉链维度表如何和事实表进行关联呢? 此时就要用到代理键,也就是在事实表和维度表中同时添加代理键,如下图所示:
完成代理键的添加后,在之后的统计中,按照代理键进行聚合即可。
事实表来源于业务事务表,代理键和业务本身没有关系,那么怎么在新增数据时在事实表中装载代理键?
当事实表中有新增数据时,新增数据中记录了维度表中原有的原子性主键,可以根据原有的主键匹配维度表中的数据,然后根据新增数据的时间范围找到匹配的代理键,然后在事实表的新增数据中加入代理键。
代理键是维度建模中极力推荐的方式,它的应用能有效的隔离源端变化带来的数仓结构 不稳定问题,同时也能够提高数据检索性能。
但是代理键维护代价非常高,尤其是数据装载过程中,对事实表带来了较大的影响, 在基于 hive 的数据仓库建设影响更加严重,比如代理键的生成、事实表中关联键的装载、 不支持非等值关联等问题,带来 ETL 过程更加复杂。
因此,在大数据体系下,谨慎使用代理键,同时对于缓慢渐变维场景,可以考虑用空 间换取时间,每天保留维表全量快照,但这样会带来存储成本,根据实际情况衡量。
3.事实表设计
3.1 事实表设计
3.1.1 增量存储
当事实表数据无状态变化时,采用增量存储,即每周期仅处理增量部分的数据,纯增量采集。
3.1.2 全量快照
状态有变化,但每天保存当前的快照数据,对于数据量在可控范围内的情况可以采用。
保存策略:
如果存储空间和成本可接受,完整存储,确保能够追溯到历史每天数据状态;
存储空间有限,考虑移动历史快照数据到冷盘,需要使用的时候可恢复;
数据历史状态数据无太大价值,可以考虑部分删除,比如近保留每月最后一天的快照数据;
3.1.3 拉链
数据量大,但缓慢变化,需要跟踪历史状态,和缓慢渐变维类似。
如果变化非常快,拉链表的数据量会大于快照表数倍,一天变一次,那么一周就保存了 7 份数据,可以考虑把已经失效的数据转移到其他的存储介质或者冷盘上,或者定期(一个 月)进行删除。
3.2 明细事实表
事实表有粒度大小之分,基于数据仓库层次架构,明细事实表一般存在于 DWD 层,该层事实表设计不进行聚合、汇总动作,仅做数据规范化、数据降维动作,同时数据保持业务 事务粒度,确保数据信息无丢失。
DWD 层与业务强相关,DWD 层的表就是业务表经过一系列规范化、降维之后的表。
3.2.1 数据降维
为了提高模型易用性,将常规维度表中的常用的属性数据冗余到相应的事实表中,从而在使用的时候避免维表关联的方式,既为数据降维。
例如,在业务中,有比较频繁的应用场景,即分析各商品类别的销量、按区域分析购买 力等,如果将商品种类、用户常住地等属性放入事实表中,当分析各商品类别的销量、按区 域分析购买力等指标时就无需再关联其他维度表。
图形5-3 原始模型
图 5-3 数据降维后的模型
3.2.2 独立维度的选择
并不是你业务中遇到的每一个实体都要成为一个独立的维度,具体哪些维度可以合并, 要根据实际的业务场景来确定,比如对于出行行业,司机一定是一个独立维度,而汽车这个 实体就没有必要称为独立的维度(除非要分析订单的取消与汽车品牌的关系),因此可以汽 车信息和司机信息进行合并。
3.2.3 事实表不一定有事实
一般将事实表中包含两部分信息:维度、度量,度量即为事实,但有些特殊情况下,事实表中无度量信息,只是记录一个实际业务动作。
比如,信息审核表:
对于出行行业,用户打车会下一单,后台系统根据下单信息筛选司机,筛选司机是一个 事实,筛选司机后指派司机,这个指派过程是一个事实,司机接单是一个事实,这些事实有些伴随着可以度量的值,有些则没有,司机接客成功、司机开始送客、行程结束后乘客支付、 乘客评价、乘客投诉都可以归为事实,但是都可能没有度量值。
3.2.4 明细事实表设计方案
设计事实表的主要依据是业务过程,之前说过,每一个业务动作事件,都可以作为一个事实,那么在一个订单处理过程中,会有多个动作,这个过程中的事实表怎么设计呢?
方案一:单事件事实表
对于每一个业务动作事件,设计一个事实表,仅记录该事件的事实以及状态。(一个 业务流程多个单事件事实表)
方案二:流程事实表
对于一个业务流程主体,设计一个事实表,跟踪整个流程的事实以及状态流转。
3.2.5 明细事实表设计案例
方案:单事件事实表
出行领域,用户下单打车,该订单的整个流程包括用户下单、司机接单、司机做单、乘 客支付,可能还伴随有评价、投诉等环节,这个场景下的明细事实表怎么设计?
按照上图的方案,针对订单这个模块,有大量的事实表,但是也有好处,这种设计方法 会使得在分析时非常明确,这样设计不存在历史订单状态变化的问题,比如 6 月 1 号 23 点 30 下单,23 点 40 司机接单,6 月 2 号 0 点到达目的地,0 点 5 分用户支付,0 点 10 分用户 评价,这种跨零点的订单,由于事实表划分明确,单个事件发生在次日,此时的历史订单状 态变化的问题就会很少,接近于无状态模型,分析就会相对简单,并且多个事实表中记录的 细节更多,把每一个环节的细节信息记录的非常全面。但是,追踪一个订单的当前进度时, 需要去查看不同的事实表的进度,不能非常直观地看到订单状态的流转。
但是,如果想通过方案一查看订单的整个运转流程,需要 join 很多表,比较复杂。
3.3 聚合事实表
相对于明细事实表,聚合事实表通常是在明细事实表的基础上,按照一定的粒度粗细进行的汇总、聚合操作,它的粒度较明细数据粒度粗,同时伴随着细节信息的丢失。 在数仓层次结构中,聚合事实表通常位于 DWS 层,一般作为通用汇总数据存在,也可以是更高粒度的指标数据。 聚合事实表的数据来源可以是两种明细事实表中的任意一种。
• 日粒度
• 周期性累积(周,月,年)
• 历史累积(累计订单量、累计金额)
3.3.1 可累加事实与不可加事实
1.1 可累加事实 可累加事实是在一定的粒度范围内,可累加的事实度量,比如:订单金额、订单数。
1.2 不可累加事实:不可累加事实是在更高粒度上不可累加的事实,比如通过率、转化率等。 通常情况下,比率这种不可累积的事实,建议拆分存储,比如通过率拆分为通过数、申请数,由细粒度数据去重计算而得到的事实,正常存储,但是更粗粒度累积是不可直接使用。
3.3.2 聚合事实表分类
3.3.2.1 公共维度层/通用汇总层
封装底层计算逻辑,做通用汇总,避免上层直接访问下层明细数据。
应对大部分可预期的、常规的数据需求,通常针对模式相对稳定的分析、BI 指标计算、 特征提取等场景,封装部分业务处理、计算逻辑,尽量避免用户直接使用底层明细数据,该 层用到的数据范围比较广泛。
通用汇总层需要满足 80%~90%的场景,对数据进行轻度汇总,避面直接访问明细层, 假设明细层有 1 亿条数据,这一层可能只有 1 千万条。
3.3.2.2 日粒度
主要应对模式稳定的分析、BI 日报、特征提取场景,同时日粒度也为后续累积计算提供粗粒度的底层,数据范围一般为上一日的数据。 对可累加指标进行粗粒度的统计,周、月等粒度的统计可以在日粒度基础上计算,假设明细层 1 亿条数据,这一层可能只有 1 百万条。 2.3 周期性累积
主要应对明确的周期性分析、BI 周期性报表,数据范围一般在某周期(周、月等)内 的。底层数据可以来自于公共维度层-通用汇总,也可以来自于日粒度。
3.3.2.3 历史累积
顾名思义,历史以来某一特定数据的累积,通常在用户画像、经营分析、特征提取方面场景较多,设计数据范围比较广泛,通常是计算耗时较长的一部分,比如某门店累积营业额、某用户累积利润贡献、用户首次下单时间(非可度量、描述性)。
3.3.3 聚合事实表案例
公共维度层(订单维度、用户维度、司机维度...): 在这个粒度,尽量覆盖更多的内容,在很多业务场景偏复杂的情况下,公共维度层能够达到 100 多个字段。
3.3.3.1 日粒度:
统计一天的数据,日粒度层与公共维度层并列。
3.3.3.2 周期性累积: 统计一定周期的数,同时可以处理一些不可累积的数据,例如比率等。
3.3.3.3 历史累积:
使用增量数据与历史数据进行累积。