离线数仓—数据仓库
前言
前面完成了数据的采集模块,下面就到了数仓项目中最重要的数据仓库模块。
一、数据仓库概述
1.数据仓库概念
数据仓库是一个为数据分析而设计的企业级数据管理系统。数据仓库可集中、整合多个信息源的大量数据,借助数据仓库的分析能力,企业可从数据中获得宝贵的信息进而改进决策。同时,随着时间的推移,数据仓库中积累的大量历史数据对于数据科学家和业务分析师也是十分宝贵的。数据仓库并不是数据的最终目的地,它可以后后续的数据分析提供支持。
2.数据仓库核心架构
二、数据仓库建模概述
1.数据仓库建模的意义
如果把数据看作图书馆里的书,我们希望看到它们在书架上分门别类地放置;如果把数据看作城市的建筑,我们希望城市规划布局合理;如果把数据看作电脑文件和文件夹,我们希望按照自己的习惯有很好的文件夹组织方式,而不是糟糕混乱的桌面,经常为找一个文件而不知所措。
数据模型就是数据组织和存储方法,它强调从业务、数据存取和使用角度合理存储数据。只有将数据有序的组织和存储起来之后,数据才能得到高性能、低成本、高效率、高质量的使用。
高性能:良好的数据模型能够帮助我们快速查询所需要的数据。例如,在数仓中,如果数据模型设计的不好,那么在实现需求的时候可能需要经过多次join操作,这是很影响性能的;良好的数据模型可以大大减少join操作,提高性能。
低成本:良好的数据模型能减少重复计算,实现计算结果的复用,降低计算成本。同高性能一样,设计一个宽表,减少重复的join计算。
高效率:良好的数据模型能极大的改善用户(下游的数据分析师等等)使用数据的体验,提高使用数据的效率。一个好的数据模型对于后续的数据分析师等来说使用起来的更加容易,能提高使用效率。
高质量:良好的数据模型能改善数据统计口径的混乱,减少计算错误的可能性。
2.数据仓库建模的理论(思想)
2.1 ER模型(了解)
数据仓库之父Bill Inmon提出的建模方法是从全企业的高度,用实体关系(Entity Relationship,ER)模型来描述企业业务,并用规范化的方式表示出来,在范式理论上符合3NF。
1)实体关系模型
实体关系模型将复杂的数据抽象为两个概念——实体和关系。实体表示一个对象,例如学生、班级,关系是指两个实体之间的关系,例如学生和班级之间的从属关系。
2)数据库规范化
数据库规范化是使用一系列范式设计数据库(通常是关系型数据库)的过程,其目的是减少数据冗余(一条相同的信息存储了多份,就叫做数据冗余),增强数据的一致性。
这一系列范式就是指在设计关系型数据库时,需要遵从的不同的规范。关系型数据库的范式一共有六种,分别是第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF)。遵循的范式级别越高,数据冗余性就越低。
3)三范式
先了解函数依赖:
第一范式:
第二范式:
上图中(学号,课名)是主键,可以推出姓名,同时学号也可以推出姓名,所以存在部分传递依赖,不符合第二范式。
第三范式:
上图中学号->系名->系主任,所以存在传递函数依赖,不符合第三范式。
下图为一个采用Bill Inmon倡导的建模方法构建的模型,从图中可以看出,较为松散、零碎,物理表数量多。
这种建模方法的出发点是整合数据,其目的是将整个企业的数据进行组合和合并,并进行规范处理,减少数据冗余性,保证数据的一致性。这种模型并不适合直接用于分析统计。
2.2 维度模型(重要)
数据仓库领域的令一位大师——Ralph Kimball倡导的建模方法为维度建模。维度模型将复杂的业务通过事实和维度两个概念进行呈现。事实通常对应业务过程,而维度通常对应业务过程发生时所处的环境。
注:业务过程可以概括为一个个不可拆分的行为事件,例如电商交易中的下单,取消订单,付款,退单等,都是业务过程。
下图为一个典型的维度模型,其中位于中心的SalesOrder为事实表,其中保存的是下单这个业务过程的所有记录。位于周围每张表都是维度表,包括Date(日期),Customer(顾客),Product(产品),Location(地区)等,这些维度表就组成了每个订单发生时所处的环境,即何人、何时、在何地下单了何种产品。从图中可以看出,模型相对清晰、简洁。
上图中,事实表SaleOrder存储的是一条下单的信息,它周围的维度表是这个下单信息所处的环境信息,描述了下单时的日期、地点、下了什么单…
维度建模以数据分析作为出发点,为数据分析服务,因此它关注的重点的用户如何更快的完成需求分析(模型简单,便于理解,用户上手更快)以及如何实现较好的大规模复杂查询的响应性能(相比于ER模型,需要join的次数大大减少,提高了性能)。
但是,维度建模也有一定的缺点。比如有数据冗余,在Date表中,一年所有的yearName都是一样的,相同的值存储了多份(如果是ER模型,会单分出去一张表存年份)。
这导致了两个问题:第一个问题是数据的存储空间问题,但是这点空间对于大数据集群来说微不足道,因为HDFS是可以无限扩展的;第二个问题是一致性问题,在修改数据的时候才可能会导致一致性问题,但是在数仓里读和写、计算都是批量执行的,不会单独修改某一条信息,因此不需要考虑数据一致性问题。
综上所述,数仓建模更适合使用维度模型,但是数仓建模不完全等同于维度模型。
二、维度建模理论之事实表
1.事实表概述
事实表作为数据仓库维度建模的核心,紧紧围绕着业务过程来设计。其包含与该业务过程有关的维度引用(维度表外键)以及该业务过程的度量值(通常是可累加的数字类型字段)。
总结来说,事实表字段=维度外键+度量值
1.1 事实表特点
事实表通常比较“细长”
列较少(只有维度外键再外加一个度量值)
行较多,且行的增速快(事实表对应的是业务过程,例如下单业务,每天都会新增,时间一长,事实表行就变长了,所以说是细长)。
1.2事实表分类
事实表有三种类型:分别是事务事实表、周期快照事实表和累积快照事实表,每种事实表都具有不同的特点和适用场景。
2.事务型事实表
2.1 事务事实表概述
事务事实表用来记录各业务过程(一个业务过程对应一个业务事实表),它保存的是各业务过程的原子操作事件,即最细粒度的操作事件。粒度是指事实表中一行数据所表达的业务细节程度。
事务型事实表可用于分析与各业务过程相关的各项统计指标,由于其保存了最细粒度的记录,可以提供最大限度的灵活性,可以支持无法预期的各种细节层次的统计需求。
例如,公司的财务部门,需要每天记账,肯定记录的是每一次的支出和收入,才能面对各种各样的统计需求,这是保存了最细粒度的数据。如果财务部门偷懒了,只记录了每一天总的收入和支出记录,虽然可以统计每天的总支出和总收入,但是如果需要统计每一天最大的一笔收入和支出,就无法统计。
2.2 事务事实表设计流程
事务事实表一般遵循以下四个步骤:选择业务过程->声明粒度->确认维度->确认事实
1)选择业务过程:
第一步是为了先确定建哪些事务型事实表。
首先,根据需求,在业务系统中,挑选我们需要的业务过程,业务过程可以概括为一个个不可拆分的行为事件,例如电商交易中的下单,取消订单,付款,退单等,都是业务过程。通常情况下,一个业务过程对应一张事务型事实表。
2)声明粒度:
第二步是为了确定一行是什么数据。
在第一步业务过程确定后,需要为每个业务过程声明粒度。即精确定义每张事务型事实表的每行数据表示什么,应该尽可能选择最细粒度,以此来应各种细节程度的需求。
典型的粒度声明如下:
订单事实表中一行数据表示的是一个订单中的一个商品项。
选择一次下单记录中的一个商品项而不是一次下单记录的原因:如果选择了一次下单记录,里面无法得知下单的商品信息,这样如果做对商品相关的统计时,无法进行。
3)确认维度:
第三步是为了确定表的列都是什么。
确定维度具体是指,确定与每张事务型事实表相关的维度有哪些(相关的列)。
确定维度时应尽量多的选择与业务过程相关的环境信息(选择的维度信息越多,后面可以统计的信息越多)。因为维度的丰富程度就决定了维度模型能够支持的指标丰富程度。
4)确定事实:
这个“事实”指的是每个业务过程的度量值(通常是可累加的数字类型的值,例如商品的次数、个数、件数、金额等)
经过上述四个步骤,事务型事实表就基本设计完成了。第一步选择业务过程可以确定有哪些事务型事实表,第二步可以确定每张事务型事实表的每行数据是什么,第三步可以确定每张事务型事实表的维度外键,第四步可以确定每张事务型事实表的度量值字段。
2.3 事务事实表的不足
事务型事实表可以保存所有业务过程的最细粒度的操作事件,故理论上其可以支撑与各业务过程相关的各种统计粒度的需求。但对于某些特定类型的需求,其逻辑可能会比较复杂,或者效率会比较低下。例如:
1)存量型指标
存量就是还有多少。例如商品库存,账户余额等。
以电商中的虚拟货币为例,按事务事实表的方法,用户获取虚拟货币的操作放到一张事务事实表中,用户使用虚拟货币的操作放到一张事务事实表中,如果要统计截至当日各用户的虚拟货币余额,需要在两张表中各自按用户分组统计总的使用和总的获取,再进行union或join得到最终的结果。这样的操作很明显效率不是很高。
可以看到,不论是从逻辑上还是效率上考虑,这都不是一个好的方案。
2)多事务关联统计
例如,现在需要统计最近30填,用户下单到支付的时间间隔的平均值。按事务型事实表来做,先要找到下单事务事实表和支付事务事实表,过滤出最近30天的记录,然后按照订单id对下单和支付进行join操作,再用支付时间减去下单时间,然后再求平均值。
这样去做逻辑上不复杂,但是涉及到了join操作,而且下单表和支付表都是大表,效率极低。
可以看到,在上述两种场景下事务型事实表的表现并不理想,需要使用别的类型的事实表去做。
3.周期型快照事实表
3.1 周期型快照事实表概述
周期快照事实表以具有规律性的、可预见的时间间隔来记录事实(记录的不是明细数据),主要用于分析一些存量型(例如商品库存,账户余额)或者状态型(空气温度,行驶速度)指标。
对于商品库存、账户余额这些存量型指标,业务系统中通常就会计算并保存最新结果,所以定期同步一份全量数据到数据仓库,构建周期型快照事实表,就能轻松应对此类统计需求,而无需再对事务型事实表中大量的历史记录进行聚合了。
对于空气温度、行驶速度这些状态型指标,由于它们的值往往是连续的,我们无法捕获其变动的原子事务操作,所以无法使用事务型事实表统计此类需求。而只能定期对其进行采样,构建周期型快照事实表。
使用场景:对于需要大量数据聚合得出结果的数据,而业务数据库中会保存最新结果的数据,可以采用周期快照事实表。
3.2 周期型快照事实表设计流程
1)确定粒度:
周期型快照事实表的粒度可由采样周期和维度描述,故确定采样周期和维度后即可确定粒度。
采样周期通常选择每日。
维度可根据统计指标决定,例如指标为统计每个仓库中每种商品的库存,则可确定维度为仓库和商品。
确定完采样周期和维度后,即可确定该表粒度为每日-仓库-商品。
总结一下,粒度=采样周期+维度
2)确认事实:
此处确认事实就是确认度量值,事实也可根据统计指标决定,例如指标为统计每个仓库中每种商品的库存,则事实为商品库存。
3.3 事实类型
此处的事实类型是指度量值的类型,而非事实表的类型。事实(度量值)共分为三类,分别是可加事实,半可加事实和不可加事实。
1)可累加事实
可加事实是指可以按照与事实表相关的所有维度进行累加,例如事务型事实表中的事实。
如下图:
图中salesAmount是该订单金额,可以按Date累加,代表一年内的订单总金额,也可以按location累加,代表该地区的订单总金额。
总之,按照与之相关的所有维度进行累加,得到的值都是有意义的。
2)半可累加事实
半可加事实是指只能按照与事实表相关的一部分维度进行累加,例如周期型快照事实表中的事实都是半可加事实。
以上述各仓库中各商品的库存每天快照事实表为例,这张表中的库存事实可以按照仓库或者商品维度进行累加,但是不能按照时间维度进行累加,因为将每天的库存累加起来是没有任何意义的。
3)不半可累加事实
不可加事实是指完全不具备可加性,例如比率型事实(作为一个事实表,它的事实字段必须具有可加性,因此需要进行转换)。不可加事实通常需要转化为可加事实,例如商品的退货率,可以转化为退单次数除以总下单次数。
4.累积型快照事实表
4.1 累积型快照事实表概述
累计快照事实表是基于一个业务流程中的多个关键业务过程联合处理而构建的事实表,如交易流程中的下单、支付、发货、确认收货业务过程。
累积型快照事实表通常具有多个日期字段,每个日期对应业务流程中的一个关键业务过程(里程碑)。
订单id | 用户id | 下单日期 | 支付日期 | 发货日期 | 确认收货日期 | 订单金额 | 支付金额 |
---|---|---|---|---|---|---|---|
1001 | 1234 | 2020-06-14 | 2020-06-15 | 2020-06-16 | 2020-06-17 | 1000 | 1000 |
累积型快照事实表主要用于分析业务过程(里程碑)之间的时间间隔等需求。例如前文提到的用户下单到支付的平均时间间隔,使用累积型快照事实表进行统计,就能避免两个事务事实表的关联操作,从而变得十分简单高效。
4.2 累积型快照事实表设计流程
累积型快照事实表的设计流程同事务型事实表类似,也可采用以下四个步骤:选择业务过程->声明粒度->确认维度->确认事实
1)选择业务过程:
选择一个业务流程中需要关联分析的多个关键业务过程,多个业务过程对应一张累积型快照事实表。
2)声明粒度:
精确定义每行数据表示的是什么,尽量选择最小粒度。
3)确认维度:
选择与各业务过程相关的维度,需要注意的是,每各业务过程均需要一个日期维度。
4)确认事实:
选择各业务过程的度量值。
三、维度建模理论之维度表
1.维度表概述
维度表在数仓建模中占据了很重要的地位。前面提到的事实表紧紧围绕着业务过程进行设计,而维度表是围绕着业务过程所处的环境进行设计。维度表主要包含一个主键和各种维度字段,维度字段又称为维度属性。
2.维度表设计步骤
1)确定维度表:
第一步首先要确定建哪些维度表。因为在设计事实表的时候,已经确定了事实表中与事实相关的维度了,因此可以一个维度就对应一张维度表。
维度表的唯一性:但是在某些情况下,可能出现多个事实表与同一个维度都相关的情况,在这种情况下,只能建立一张维度表。
维度退化:某些维度表中的字段过少,例如支付方式表,只有一个id和支付方式名称,这样的维度通常不创建维度表,直接把该表的维度属性(字段)增加到与之相关的事实表中。
2)确定主维表和相关维表:
主维表和相关维表值得是业务系统中与某维度相关的表。例如业务系统中与商品相关的表有sku_info,spu_info,base_trademark,base_category3,base_category2,base_category1等,其中sku_info就称为商品维度的主维表,其余表成为商品维度的相关维表。维度表的维度通常与主维表相同。
3)确定维度属性:
确定维度属性即确定维度表字段。维度属性主要来自于业务系统中与该维度对应的主维表和相关维表。维度属性可直接从主维表或相关维表中选择(可以直接获得的),也可通过进一步加工得到(没有办法直接用)。
在确定维度属性时,需要遵循以下要求:
尽可能生成丰富的维度属性:维度属性在后续做分析中有重要作用,例如查询、分组聚合、过滤条件等等。维度属性的丰富程度影响了后续统计指标的丰富程度。
尽量不使用编码(例如支付表的支付方式字段,在业务数据库中一般用编码进行存储),而使用明确的文字说明,在数仓中一般可以编码和文字共存的方式,一个字段写编码,一个字段写编码对应的名称。
尽量沉淀出通用的维度属性:有些维度属性的获取方式比较复杂,没有办法直接拿到,例如需要多个字段进行拼接等等,为了方便后续的使用同时避免重复处理,可以将这些维度属性沉淀到维度表中。
例如:
字段condition_amount和字段benefit_amount组成满多少钱减多少钱的规则,字段condition_num和字段benefit_discount组成满几件打几折的规则。像优惠规则这样的字段无法直接获得,每次使用都需要计算才能得出来,因此在这里将优惠规则字段沉淀出来。
3.维度设计要点
3.1 规范化和反规范化
规范化是指使用一系列范式设计数据库的过程,其目的是减少数据冗余,增强数据的一致性。通常情况下,规范化之后,一张表的字段会拆分到多张表。
反规范化是指将多张表的数据冗余到一张表,其目的是减少join操作,提高查询性能。
在设计维度表时,如果对其进行规范化,得到的维度模型称为雪花模型,如果对其进行反规范化,得到的模型称为星型模型。
3.2 雪花模型和星型模型
数据仓库的主要目的是用于数据分析和统计,所以是否方便统计分析是非常重要的。如果采用雪花模型,数仓的统计分析过程需要大量的关联操作,使用的复杂度较高,同时性能较差;如果采用星型模型,使用起来比较方便而且性能较好。所以在数仓中一般采用星型模型。
3.3 维度变化
维度属性通常不是静态的,而是会随时间变化的,数据仓库的一个重要特点就是反映历史的变化,所以如何保存维度的历史状态是维度设计的重要工作之一。保存维度数据的历史状态,通常有以下两种做法,分别是全量快照表和拉链表。
1)全量快照表
离线数据仓库的计算周期通常为每天一次,所以可以每天保存一份全量的维度数据。这种方式的优点和缺点都很明显。
优点是简单而有效,开发和维护成本低,且方便理解和使用(去对应分区拉取数据即可)。
缺点是浪费存储空间,尤其是当数据的变化比例比较低时。
缺点当一个表中的数据量比较大,而且变化的比例比较低的时候,不适合采用这种方式,于是出现了拉链表。
2)拉链表
拉链表的意义是能够更加高效的保存维度信息的历史状态。
为什么要做拉链表:
如何使用拉链表;
3.4 多值维度
如果事实表中一条记录在某个维度表中有多条记录与之对应,称为多值维度。例如一个订单,可能包含多个订单商品,所以就会对应商品维度表中的多条数据。
针对这种情况,有以下两种解决方式:
方式一:降低事实表的维度,例如将订单表的维度从一条订单降低为一条订单中的一个商品项。
方式二:在事实表中使用多个字段来保存多个维度值,这种方式适用于多值维度个数固定的情况。
所以,一般采用第一种方案来解决多值维度问题。
3.5 多值属性
维度表中的某个属性同时有多个值,称为“多值属性”。例如商品维度的平台属性和销售属性,每个商品都具有多个属性值。
针对这种情况,采用以下两种方式解决:
方式一:将多值属性放到一个字段里,采用类似map的结构key1:value1,key2:value2的形式,例如将一个手机商品的平台属性设置为“品牌:鸿蒙,CPU:麒麟990”。
方式二:将多值属性放到多个字段,每个字段对应一个属性。这种方案只适用于多值属性个数固定的情况。