阿里巴巴大数据实践(数据模型篇)
第8章 大数据领域建模综述
第9章 数据整合及管理体系
第10章 维度设计(重点!)
第11章 事实表设计
第8章 大数据领域建模综述
8.1 为什么需要数据建模
随着DT时代互联网、智能设备及其他信息技术的发展,数据爆发式增长,如何将这些数据进行有序、有结构地分类组织和存储是我们面临的一个挑战。
如果把数据看作图书馆里的书,我们希望看到它们在书架上分门别类地放置;如果把数据看作城市的建筑,我们希望城市规划布局合理:如果把数据看作电脑文件和文件夹,我们希望按照自己的习惯有很好的文件夹组织方式,而不是糟糕棍乱的桌面,经常为找一个文件而不知所措。
数据模型就是数据组织和存储方法,它强调从业务、数据存取和使用角度合理存储数据。Linux的创始人Torvalds有一段关于“什么才是优秀程序员”的话:“烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系”,其阐述了数据模型的重要性。有了适合业务和基础数据存储环境的模型,那么大数据就能获得以下好处。
-
性能
良好的数据模型能帮助我们快速查询所需要的数据,减少数据的I/O吞吐。 -
成本
良好的数据模型能极大地减少不必要的数据冗余,也能实现计算结果复用,极大地降低大数据系统中的存储和计算成本。 -
效率
良好的数据模型能极大地改善用户使用数据的体验,提高使用数据的效率。 -
质量
良好的数据模型能改善数据统计口径的不一致性,减少数据计算错误的可能性。
因此,毋庸置疑,大数据系统需要数据模型方法来帮助更好地组织和存储数据,以便在性能、成本、效率和质量之间取得最佳平衡。
8.2 关系数据库系统和数据仓库
8.3 从OLTP和OLAP系统的区别看模型方法论的选择
OLTP系统通常面向的主要数据操作是随机读写,主要采用满足3NF的实体关系模型存储数据,从而在事务处理中解决数据的冗余和一致性问题;而OLAP系统面向的主要数据操作是批量读写,事务处理中的一致性不是OLAP所关注的,其主要关注数据的整合,以及在一次性的复杂大数据查询和处理中的性能,因此它需要采用一些不同的数据建模方法。
8.4 典型的数据仓库建模方法论
8.4.1 ER模型
数据仓库之父Bill Inmon提出的建模方法是从全企业的高度设计一个3NF模型,用实体关系(EntityRelationship, ER)模型描述企业业务,在范式理论上符合3NF。数据仓库中的3NF与OLTP系统中的3NF的区别在于,它是站在企业角度面向主题的抽象,而不是针对某个具体业务流程的实体对象关系的抽象。
采用ER模型建设数据仓库模型的出发点是整合数据,将各个系统中的数据以整个企业角度按主题进行相似性组合和合并,并进行一致性处理,为数据分析决策服务,但是并不能直接用于分析决策。
8.4.2 维度模型
维度模型是数据仓库领域的Ralph Kimball大师所倡导的,他的TheData Warehouse Toolkit-The Complete Guide to Dimensional Modeling是数据仓库工程领域最流行的数据仓库建模的经典。
维度建模从分析决策的需求出发构建模型,为分析需求服务,因此它重点关注用户如何更快速地完成需求分析,同时具有较好的大规模复杂查询的响应性能。其典型的代表是星形模型,以及在一些特殊场景下使用的雪花模型。其设计分为以下几个步骤。
- 选择需要进行分析决策的业务过程
业务过程可以是单个业务事件,比如交易的支付、退款等;也可以是某个事件的状态,比如当前的账户余额等;还可以是一系列相关业务事件组成的业务流程,具体需要看我们分析的是某些事件发生情况,还是当前状态,或是事件流转效率。
- 选择粒度
在事件分析中,我们要预判所有分析需要细分的程度,从而决定选择的粒度。粒度是维度的一个组合。
- 识别维表
选择好粒度之后,就需要基于此粒度设计维表,包括维度属性,用于分析时进行分组和筛选。
- 选择事实
确定分析需要衡量的指标。
8.4.3 Data Vault模型
Data Vault是DanLinstedt发起创建的一种模型,它是ER模型的衍生,其设计的出发点也是为了实现数据的整合,但不能直接用于数据分析决策。它强调建立一个可审计的基础数据层,也就是强调数据的历史性、可追溯性和原子性,而不要求对数据进行过度的一致性处理和整合;同时它基于主题概念将企业数据进行结构化组织,并引入了更进一步的范式处理来优化模型,以应对游、系统变更的扩展性。
8.4.4 Anchor模型
Anchor对DataVault模型做了进一步规范化处理,Lars.Ri:innback 的初衷是设计一个高度可扩展的模型,其核心思想是所有的扩展只是添加而不是修改,因此将模型规范到6NF,基本变成了k-v结构化模型。
8.5 阿里巴巴数据模型实践综述
阿里巴巴数据公共层建设的指导方法是一套统一化的集团数据整合及管理的方法体系(在内部这一体系称为“OneData”),其包括一致性的指标定义体系、模型设计方法体系以及配套工具。
第9章 数据整合及管理体系
面对爆炸式增长的数据,如何建设高效的数据模型和体系,对这些数据进行有序和有结构地分类组织和存储,避免重复建设和数据不一致性,保证数据的规范性,一直是大数据系统建设不断追求的方向。
OneData即是阿里巴巴内部进行数据整合及管理的方法体系和工具。阿里巴巴的大数据工程师在这一体系下,构建统一、规范、可共享的全域数据体系,避免数据的冗余和重复建设,规避数据烟囱和不一致性,充分发挥阿里巴巴在大数据海量、多样性方面的独特优势。
借助这一统一化数据整合及管理的方法体系,我们构建了阿里巴巴的数据公共层,并可以帮助相似的大数据项目快速落地实现。
图9.1 体系架构图.png9.2 规范定义
规范定义指以维度建模作为理论基础,构建总线矩阵,划分和定义数据域、业务过程、维度、度量/原子指标、修饰类型、修饰词、时间周期、派生指标。
图9.2 规范定义实例.png9.3 模型设计
阿里巴巴的数据团队把表数据模型分为三层:
- 操作数据层(ODS)
数据同步、清洗、保存历史
- 公共维度模型层(CDM)
存放明细事实数据、维表数据及公共指标汇总数据,包括:
明细数据层(DWD):一般根据ODS层数据加工生成
汇总数据层(DWS):一般根据维表数据和明细事实数据加工生成
采用维度模型方法作为理论基础,更多地采用一些维度退化手法,将维度退化至事实表中(缓慢变化维),减少事实表和维表的关联,提高明细数据表的易用性:同时在汇总数据层,加强指标的维度退化,采取更多的宽表化手段构建公共指标数据层,提升公共指标的复用性,减少重复加工。
主要功能:
组合相关和相似数据:采用明细宽表,复用关联计算,减少数据扫描。
公共指标统一加工:基于OneData体系构建命名规范、口径一致和算法统一的统计指标,为上层数据产品、应用和服务提供公共指标;建立逻辑汇总宽表。
建立一致性维度:建立一致的数据分析维表,降低数据计算口径、算法不统一的风险。
- 应用数据层(ADS)
存放数据产品个性化的统计指标数据,根据CDM层与ODS层加工生成。
主要功能:
个性化指标加工:不公用性、复杂性(指数型、比值型、排名型指标)。
基于应用的数据组装:大宽表集市、横表转纵表、趋势指标串。
9.3.3 基本原则
- 高内聚和低辑合
一个逻辑或者物理模型由哪些记录和字段组成,应该遵循最基本的软件设计方法论的高内聚和低藕合原则。
主要从数据业务特性和访问特性两个角度来考虑:将业务相近或者相关、粒度相同的数据设计为一个逻辑或者物理模型:将高概率同时访问的数据放一起,将低概率同时访问的数据分开存储。
- 核心模型与扩展模型分离
建立核心模型与扩展模型体系,核心模型包括的字段支持常用的核心业务,扩展模型包括的字段支持个性化或少量应用的需要,不能让扩展模型的字段过度侵入核心模型,以免破坏核心模型的架构简洁性与可维护性。
- 公共处理逻辑下沉及单一
越是底层公用的处理逻辑越应该在数据调度依赖的底层进行封装与实现,不要让公用的处理逻辑暴露给应用层实现,不要让公共逻辑多处同时存在。
- 成本与性能平衡
适当的数据冗余可换取查询和刷新性能,不宜过度冗余与数据复制。
- 数据可回滚
处理逻辑不变,在不同时间多次运行数据结果确定不变。
- 一致性
具有相同含义的字段在不同表中的命名必须相同,必须使用规范定义中的名称。
- 命名清晰、可理解
表命名需清晰、一致,表名需易于消费者理解和使用。
9.4 模型实施
9.4.1 业界常用的模型实施过程
- Kimball模型实施过程
Kimball维度建模主要探讨需求分析、高层模型、详细模型和模型审查整个过程。
- Inmon模型实施过程
Inmon对数据模型的定位是:扮演着通往数据仓库其他部分的智能路线图的角色。由于数据仓库的建设不是一蹦而就的,为了协调不同人员的工作以及适应不同类型的用户,非常有必要建立一个路线图——数据模型,描述数据仓库各部分是如何结合在一起的。
- 其他模型实施过程
业务建模:生成业务模型,主要解决业务层面的分解和程序化。
领域建模:生成领域模型,主要是对业务模型进行抽象处理,生成领域概念模型。
逻辑建模:生成逻辑模型,主要是将领域模型的概念实体以及实体之间的关系进行数据库层次的逻辑化。
物理建模:生成物理模型,主要解决逻辑模型针对不同关系数据库的物理化以及性能等一些具体的技术问题。
9.4.2 OneData实施过程
图9.11 实施工作流.png- 数据调研
业务调研:了解各个业务领域、业务线的业务有什么共同点和不同点,以及各个业务线可以细分为哪几个业务模块,每个业务模块具体的业务流程又是怎样的。业务调研是否充分,将会直接决定数据仓库建设是否成功。
需求调研:收集数据使用者的需求,可以去找分析师、业务运营人员了解他们有什么数据诉求,此时更多的就是报表需求。
- 架构设计
数据域划分:数据域是指面向业务分析,将业务过程或者维度进行抽象的集合。业务过程可以概括为一个个不可拆分的行为事件,如下单、支付、退款。为保障整个体系的生命力,数据域需要抽象提炼,并且长期维护和更新,但不轻易变动。在划分数据域时,既能涵盖当前所有的业务需求,又能在新业务进入时无影响地被包含进已有的数据域中或者扩展新的数据域。
构建总线矩阵:明确每个数据域下有哪些业务过程;业务过程与哪些维度相关,并定义每个数据域下的业务过程和维度。
- 规范定义
规范定义主要定义指标体系,包括原子指标、修饰词、时间周期和派生指标。
- 模型设计
模型设计主要包括维度及属性的规范定义,维表、明细事实表和汇总事实表的模型设计。
- 总结
OneData的实施过程是一个高度迭代和动态的过程,一般采用螺旋式实施方法。在总体架构设计完成之后,开始根据数据域进行迭代式模型设计和评审。在架构设计、规范定义和模型设计等模型实施过程中,都会引人评审机制,以确保模型实施过程的正确性。
第10章 维度设计(重点!)
10.1 维度设计基础
10.1.1 维度的基本概念
维度是维度建模的基础和灵魂。在维度建模中,将度量称为“事实”,将环境描述为“维度”,维度是用于分析事实所需要的多样环境。例如,在分析交易过程时,可以通过买家、卖家、商品和时间等维度描述交易发生的环境。
维度所包含的表示维度的列,称为维度属性。维度属性是查询约束条件、分组和报表标签生成的基本来源,是数据易用性的关键。例如,在查询请求中,获取某类目的商品、正常状态的商品等,是通过约束商品类目属性和商品状态属性来实现的;统计淘宝不同商品类目的每日成交金额,是通过商品维度的类目属性进行分组的;我们在报表中看到的类目、BC类型(B指天猫,C指集市)等,都是维度属性。所以维度的作用一般是查询约束、分类汇总以及排序等。
如何获取维度或维度属性:一方面,可以在报表中获取;另一方面,可以在和业务人员的交谈中发现维度或维度属性。因为它们经常出现在查询或报表请求中的“按照”(by)语句内。例如,用户要“按照”月份和产品来查看销售情况,那么用来描述其业务的自然方法应该作为维度或维度属性包括在维度模型中。
维度使用主键标识其唯一性,主键也是确保与之相连的任何事实表之间存在引用完整性的基础。主键有两种:代理键和自然键,它们都是用于标识某维度的具体值。但代理键是不具有业务含义的键,一般用于处理缓慢变化维;自然键是具有业务含义的键。比如商品,在ETL过程中,对于商品维表的每一行,可以生成一个唯一的代理键与之对应;商品本身的自然键可能是商品ID等。其实对于前台应用系统来说,商品ID是代理键:而对于数据仓库系统来说,商品ID则属于自然键。
10.1.2 维度的基本设计方法
维度的设计过程就是确定维度属性的过程,如何生成维度属性,以及所生成的维度属性的优劣,决定了维度使用的方便性,成为数据仓库易用性的关键。正如Kimball所说的,数据仓库的能力直接与维度属性的质量和深度成正比。
下面以淘宝的商品维度为例对维度设计方法进行详细说明。
- 选择维度或新建维度
作为维度建模的核心,在企业级数据仓库中必须保证维度的唯一性。以淘宝商品维度为例,有且只允许有一个维度定义。
- 确定主维表
此处的主维表一般是ODS表,直接与业务系统同步。以淘宝商品维度为例,s_auction_ auctions是与前台商品中心系统同步的商品表,此表即是主维表。
- 确定相关维表
数据仓库是业务源系统的数据整合,不同业务系统或者同一业务系统中的表之间存在关联性。根据对业务的梳理,确定哪些表和主维表存在关联关系,并选择其中的某些表用于生成维度属性。
以淘宝商品维度为例,根据对业务逻辑的梳理,可以得到商品与类目、SPU、卖家、店铺等维度存在关联关系。
- 确定维度属性
本步骤主要包括两个阶段:
(1)从主维表中选择维度属性或生成新的维度属性
(2)从相关维表中选择维度属性或生成新的维度属性
以淘宝商品维度为例,从主维表(s_auction_auctions)和类目、SPU、卖家、店铺等相关维表中选择维度属性或生成新的维度属性。
确定维度属性的关键点:
- 尽可能生成丰富的维度属性
比如淘宝商品维度有近百个维度属性,为下游的数据统计、分析、探查提供了良好的基础。
- 尽可能多地给出包括一些富有意义的文字性描述
属性不应该是编码,而应该是真正的文字。在阿里巴巴维度建模中,一般是编码和文字同时存在,比如商品维度中的商品ID和商品标题、类目ID和类目名称等。ID一般用于不同表之间的关联,而名称一般用于报表标签。
- 区分数值型属性和事实
数值型字段是作为事实还是维度属性,可以参考字段的一般用途。如果通常用于查询约束条件或分组统计,则是作为维度属性;如果通常用于参与度量的计算,则是作为事实。
比如商品价格,可以用于查询约束条件或统计价格区间的商品数量,此时是作为维度属性使用的;也可以用于统计某类目下商品的平均价格,此时是作为事实使用的。
另外,如果数值型字段是离散值,则作为维度属性存在的可能性较大;如果数值型字段是连续值,则作为度量存在的可能性较大,但并不绝对,需要同时参考字段的具体用途。
- 尽量沉淀出通用的维度属性
有些维度属性获取需要进行比较复杂的逻辑处理,有些需要通过多表关联得到,或者通过单表的不同字段混合处理得到,或者通过对单表的某个字段进行解析得到。此时,需要将尽可能多的通用的维度属性进行沉淀。一方面,可以提高下游使用的方便性,减少复杂度;另一方面,可以避免下游使用解析时由于各自逻辑不同而导致口径不一致。
例如,淘宝商品的property字段,使用key:value方式存储多个商品属性。商品品牌就存存储在此字段中,而商品品牌是重要的分组统计和查询约束的条件,所以需要将品牌解析出来,作为品牌属性存在。例如,商品是否在线,即在淘宝网站是否可以查看到此商品,是重要的查询约束的条件,但是无法直接获取,需要进行加工,加工逻辑是:商品状态为0和1且商品上架时间小于或等于当前时间,则是在线商品;否则是非在线商品。所以需要封装商品是否在线的逻辑作为一个单独的属性字段。
10.1.3 维度的层次结构
维度中的一些描述属性以层次方式或一对多的方式相互关联,可以被理解为包含连续主从关系的属性层次。层次的最底层代表维度中描述最低级别的详细信息,最高层代表最高级别的概要信息。维度常常有多个这样的嵌入式层次结构。
比如淘宝商品维度,有卖家、类目、品牌等。商品属于类目,类目属于行业,其中类目的最低级别是叶子类目,叶子类目属于二级类目,二级类目属于一级类目。
在属性的层次结构中进行钻取是数据钻取的方法之一。
假设已有一个淘宝交易订单,创建事实表。现在统计2015年“双11”的下单GMV,得到一行记录;沿着层次向下钻取,添加行业,得到行业实例个数的记录数;继续沿着层次向下钻取,添加一级类目,得到一级类目实例个数的记录数。可以看到,通过向报表中添加连续的维度细节级别,实现在层次结构中进行钻取。
(最高层次统计+钻取至行业层次+钻取至一级类目层次)
| 日期 | 行业 | 一级类目 | 下单GMV |
| -------- | ----- | -------- | ------------------ |
| 20151111 | ALL | ALL | 912 亿 |
| 20151111 | 行业1 | ALL | industry1_gmv |
| 20151111 | 行业1 | cat1 | industry1_cat1_gmv |
10.1.4 规范化和反规范化
当属性层次被实例化为一系列维度,而不是单一的维度时,被称为雪花模式。大多数联机事务处理系统(OLTP)的底层数据结构在设计时采用此种规范化技术,通过规范化处理将重复属性移至其自身所属的表中,删除冗余数据。
这种方法用在OLTP系统中可以有效避免数据冗余导致的不一致性。比如在OLTP系统中,存在商品表和类目表,且商品表中有冗余的类目表的属性字段,假设对某类目进行更新,则必须更新商品表和类目表,且由于商品和类目是一对多的关系,商品表可能每次需要更新几十万甚至上百万条记录,这是不合理的。而对于联机分析处理系统(OLAP) 来说,数据是稳定的,不存在OLTP系统中所存在的问题。
图10.1 采用雪花模式规范化处理淘宝商品维度所表现的形式.png 图10.2 反规范化处理淘宝商品维度表现的形式.png将维度的属性层次合并到单个维度中的操作称为反规范化。分析系统的主要目的是用于数据分析和统计,如何更方便用户进行统计分析决定了分析系统的优劣。
采用雪花模式,用户在统计分析的过程中需要大量的关联操作,使用复杂度高,同时查询性能很差;而采用反规范化处理,则方便、易用且性能好。
10.1.5 一致性维度和交叉探查
构建企业级数据仓库不可能一蹦而就,一般采用迭代式的构建过程。而单独构建存在的问题是形成独立型数据集市,导致严重的不一致性。Kimball的数据仓库总线架构提供了一种分解企业级数据仓库规划任务的合理方法,通过构建企业范围内一致性维度和事实来构建总线架构。
数据仓库总线架构的重要基石之一就是一致性维度。在针对不同数据域进行迭代构建或并行构建时,存在很多需求是对于不同数据域的业务过程或者同一数据域的不同业务过程合并在一起观察。
比如对于日志数据域,统计了商品维度的最近一天的PV和UV;对于交易数据域,统计了商品维度的最近一天的下单GMV。现在将不同数据域的商品的事实合并在一起进行数据探查,如计算转化率等,称为交叉探查。
如果不同数据域的计算过程使用的维度不一致,就会导致交叉探查存在问题。当存在重复的维度,但维度属性或维度属性的值不一致时,会导致交叉探查无法进行或交叉探查结果错误。
维度不一致主要分为两种类型:
- 维度格式不一致
假设商品维度1的商品上架时间这一维度属性时间格式是yyyy-MM-ddHH:mm:ss,商品维度2的商品上架时间这一维度属性时间格式是UNIXtimestamp,进行交叉探查时如果需要根据商品上架时间做限制,则复杂性较高。
- 内容不一致
假设对于日志数据域,统计使用的是商品维度1;对于交易数据域,统计使用的是商品维度2。商品维度1包含维度属性BC类型,而商品维度2无此属性,则无法在BC类型上进行交叉探查;商品维度1不包含阿里旅行的商品,商品维度2包含全部的淘系商品,交叉探查也无法进行。
维度一致性的表现形式:
- 共享维表
比如在阿里巴巴的数据仓库中,商品、卖家、买家、类目等维度有且只有一个。所以基于这些公共维度进行的交叉探查不会存在任何问题。
- 一致性上卷
其中一个维度的维度属性是另一个维度的维度属性的子集,且两个维度的公共维度属性结构和内容相同。
比如在阿里巴巴的商品体系中,有商品维度和类目维度,其中类目维度的维度属性是商品维度的维度属性的子集,且有相同的维度属性和维度属性值。这样基于类目维度进行不同业务过程的交叉探查也不会存在任何问题。
- 交叉属性
两个维度具有部分相同的维度属性。
比如在商品维度中具有类目属性,在卖家维度中具有主营类目属性,两个维度具有相同的类目属性,则可以在相同的类目属性上进行不同业务过程的交叉探查。
10.2 维度设计高级主题
10.2.1 维度整合
数据仓库的定义:数据仓库是一个面向主题的、集成的、非易失的且随时间变化的数据集合,用来支持管理人员的决策。其中集成是数据仓库的四个特性中最重要的一个。
各个应用系统之间存在差异:
- 编码、命名习惯、度量单位
性别(0/1和M/F)、用户id(user和user_id)、金额单位(元和分)
- 不同的物理实现
应用出于性能和扩展性的考虑,或者随技术架构的演变,以及业务的发展,采用不同的物理实现。拆分至不同类型数据库中,部分数据采用关系型数据库存储(如Oracle、MySQL等),部分数据采用NoSQL数据库存储(如HBase、Tair等)。
拆分成同一类型数据库中的多个物理表,比如对于淘宝商品,有商品主表和商品扩展表,商品主表存储商品基本信息,商品扩展表存储商品特殊信息,如不同产品线的定制化信息等;对于淘宝会员,有会员主表和会员扩展表,会员主表存储用户基本信息,会员扩展表存储用户扩展信息,如用户的各种标签信息等。
因此,数据由面向应用的操作型环境进入数据仓库后,需要进行数据集成。
将面向应用的数据转换为面向主题的数据仓库数据,本身就是一种集成,具体表现在:
-
统一命名规范(表名、字段名等)
-
统一字段类型(相同和相似字段的字段类型)
-
统一公共代码及代码值(公共代码及标志性字段的数据类型、命名方式等)
-
统一业务含义相同的表
主要依据高内聚、低稠合的理念,在物理实现中,将业务关系大、源系统影响差异小的表进行整合:将业务关系小、游、系统影响差异大的表进行分而置之。通常有如下几种集成方式:
(1)采用主从表的设计方式,将两个表或多个表都有的字段放在主表中(主要基本信息),从属信息分别放在各自的从表中。对于主表中的主键,要么采用复合主键、源主键和系统或表区别标志:要么采用唯一主键、“源主键和系统或表区别标志”生成新的主键。通常建议采用复合主键的方式。
(2)直接合并,共有信息和个性信息都放在同一个表中。如果表字段的重合度较低,则会出现大量空值,对于存储和易用性会有影响,需谨慎选择。
(3)不合并,因为源表的表结构及主键等差异很大,无法合并,使用数据仓库里的多个表存放各自的数据。
表级别的整合有两种表现形式:
- 垂直整合
不同的来源表包含相同的数据集,只是存储的信息不同。
比如淘宝会员在源系统中有多个表,如会员基础信息表、会员扩展信息表、淘宝会员等级信息表、天猫会员等级信息表,这些表都属于会员相关信息表,依据维度设计方法,尽量整合至会员维度模型中,丰富其维度属性。
- 水平整合
不同的来源表包含不同的数据集,不同子集之间无交叉,也可以存在部分交叉。
比如针对蚂蚁金服的数据仓库,其采集的会员数据有淘宝会员、1688会员、国际站会员、支付宝会员等,是否需要将所有的会员整合到一个会员表中呢?如果进行整合,首先需要考虑各个会员体系是否有交叉,如果存在交叉,则需要去重;如果不存在交叉,则需要考虑不同子集的自然键是否存在冲突,如果不冲突,则可以考虑将各子集的自然键作为整合后的表的自然键;另一种方式是设置超自然键,将来源表各子集的自然键加工成一个字段作为超自然键。在阿里巴巴,通常采用将来源表各子集的自然键作为联合主键的方式,并且在物理实现时将来源字段作为分区字段。
10.2.2 水平拆分
维度通常可以按照类别或类型进行细分。
比如淘系商品表,根据业务线或行业等可以对商品进行细分,如淘宝的商品、天猫的商品、1688的商品、飞猪旅行的商品、淘宝海外的商品、天猫国际的商品等。
不同分类的商品,其维度属性可能相同,也可能不同。
比如航旅的商品和普通的淘系商品,都属于商品,都有商品价格、标题、类型、上架时间、类目等维度属性,但是航旅的商品除了有这些公共属性外,还有酒店、景点、门票、旅行等自己独特的维度属性。
维度设计方法:
-
将维度的不同分类实例化为不同的维度,同时在主维度中保存公共属性
-
维护单一维度,包含所有可能的属性
数据模型设计过程中需要考虑的因素有很多,基本不可能满足各个特性指标的最优化。在设计过程中需要重点考虑以下三个原则:
- 扩展性
当源系统、业务逻辑变化时,能通过较少的成本快速扩展模型,保持核心模型的相对稳定性。软件工程中的高内聚、低稠合的思想是重要的指导方针之一。
- 效能
在性能和成本方面取得平衡。通过牺牲一定的存储成本,达到性能和逻辑的优化。
- 易用性
模型可理解性高、访问复杂度低。用户能够方便地从模型中找到对应的数据表,并能够方便地查询和分析。
根据数据模型设计思想,在对维度进行水平拆分时,主要考虑如下两个依据:
- 维度的不同分类的属性差异情况
当维度属性随类型变化较大时,将所有可能的属性建立在一个表中是不切合实际的,也没有必要这样做,此时建议采用方案1。定义一个主维度用于存放公共属性;同时定义多个子维度,其中除了包含公共属性外,还包含各自的特殊属性。
比如在阿里巴巴数据仓库维度体系中,依据此方法,构建了商品维度、航旅商品维度等。公共属性一般比较稳定,通过核心的商品维度,保证了核心维度的稳定性;通过扩展子维度的方式,保证了模型的扩展性。
- 业务的关联程度
两个相关性较低的业务,稠合在一起弊大于利,对模型的稳定性和易用性影响较大。
比如在阿里巴巴数据仓库维度体系中,对淘系商品和1688商品构建两个维度。虽然淘系和1688在底层技术实现上是统一的,但属于不同的BU,业务各自发展;在数据仓库层面,淘系和1688属于不同的数据集市,一般不会相互调用,业务分析人员一般只针对本数据集市进行统计分析。如果设计成一个维度,由于不同BU业务各自发展,1688业务变更,此维度需要变更,淘宝业务变更亦然,稳定性很差;在易用性方面,会给数据使用方造成困扰。
10.2.3 垂直拆分
在维度设计内容中,我们提到维度是维度建模的基础和灵魂,维度属性的丰富程度直接决定了数据仓库的能力。在进行维度设计时,依据维度设计的原则,尽可能丰富维度属性,同时进行反规范化处理。
对于具体实现时可能存在的问题,一是在“水平拆分”中提到的,由于维度分类的不同而存在特殊的维度属性,可以通过水平拆分的方式解决此问题。二是某些维度属性的来源表产出时间较早,而某些维度属性的来源表产出时间较晚;或者某些维度属性的热度高、使用频繁,而某些维度属性的热度低、较少使用;或者某些维度属性经常变化,而某些维度属性比较稳定。在“水平拆分”中提到的模型设计的三个原则同样适合解决此问题。
出于扩展性、产出时间、易用性等方面的考虑,设计主从维度。主维表存放稳定、产出时间早、热度高的属性;从维表存放变化较快、产出时间晚、热度低的属性。
比如在阿里巴巴数据仓库中,设计了商品主维度和商品扩展维度。其中商品主维度在每日的1:30左右产出,而商品扩展维度由于有冗余的产出时间较晚的商品品牌和标签信息,在每日的3:00左右产出。另外,由于商品扩展维度有冗余的库存等变化较快的数据,对于主维度进行缓慢变化的处理较为重要。通过存储的冗余和计算成本的增加,实现了商品主模型的稳定和产出时间的提前,对于整个数据仓库的稳定和下游应用的产出都有较大意义。
10.2.4 历史归档
在数据仓库中,可以借用前台数据库的归档策略,定期将历史数据归档至历史维表。在实践中,阿里巴巴数据仓库设计了商品维表和历史商品维表,每天将历史数据归档至历史商品维表。
归档策略:
- 数据仓库中实现前台归档算法,定期对历史数据进行归档
存在一些问题,一是前台归档策略复杂,实现成本较高;二是前台归档策略可能会经常变化,导致数据仓库归档算法也要随之变化,维护和沟通成本较高。此方式适用于前台归档策略逻辑较为简单,且变更不频繁的情况。
- 同前台归档策略,但采用数据库变更日志的方式
对于如此庞大的数据量,阿里巴巴采用的数据抽取策略一般是通过数据库binlog日志解析获取每日增量,通过增量merge全量的方式获取最新的全量数据。可以使用增量日志的删除标志,作为前台数据归档的标志。通过此标志对数据仓库的数据进行归档。此方式不需要关注前台归档策略,简单易行。但对前台应用的要求是数据库的物理删除只有在归档时才执行,应用中的删除只是逻辑删除。
- 数据仓库自定义归档策略
可以将归档算法用简单、直接的方式实现,但原则是尽量比前台应用晚归档、少归档。避免出现数据仓库中已经归档的数据再次更新的情况。
10.3 维度变化
10.3.1 缓慢变化维
数据仓库的重要特点之一是反映历史变化,所以如何处理维度的变化是维度设计的重要工作之一。
缓慢变化维的提出是因为在现实世界中,维度的属性并不是静态的,它会随着时间的流逝发生缓慢的变化。与数据增长较为快速的事实表相比,维度变化相对缓慢。
在一些情况下,保留历史数据没有什么分析价值;而在另一些情况下,保留历史数据将会起到至关重要的作用。在Kimball的理论中,有三种处理缓慢变化维的方式(以某商品改变所属类目为例,分为订单表和商品类目维表,图略):
- 重写维度值
不保留历史数据,始终取最新数据。
(此例中商品类目维表直接将商品所属类目从类目1变为类目2,订单表的商品key不变)
- 插入新的维度行
保留历史数据,维度值变化前的事实和过去的维度值关联,维度值变化后的事实和当前的维度值关联。
(此例中在商品类目维表中插入一条新商品记录,订单表中历史记录的商品key为旧商品记录,新增记录的商品key为新商品记录)
- 添加维度列
采用第二种处理方式不能将变化前后记录的事实归一为变化前的维度或者归一为变化后的维度。
比如根据业务需求,需要将11月份的交易金额全部统计到类目2上,采用第二种处理方式无法实现。针对此问题,采用第三种处理方式,保留历史数据,可以使用任何一个属性列。
(此例中订单表不变,在商品类目维表中将原类目改为旧类目,并新增新类目)
- 新增维度的同时,更新历史数据
10.3.2 快照维表
在“维度的基本概念”中,介绍了自然键和代理键的定义,在Kimball的维度建模中,必须使用代理键作为每个维表的主键,用于处理缓慢变化维。
不使用代理键如何处理缓慢变化维的问题。在阿里巴巴数据仓库实践中,处理缓慢变化维的方法是采用快照方式。数据仓库的计算周期一般是每天一次,基于此周期,处理维度变化的方式就是每天保留一份全量快照数据。
比如商品维度,每天保留一份全量商品快照数据。任意一天的事实均可以获取到当天的商品信息,也可以获取到最新的商品信息,通过限定日期,采用自然键进行关联即可。此方法优点为简单有效和使用方便(只需要限定日期,即可获取到当天的维度快照数据),弊端主要体现在存储的极大浪费上,每天的变化量占总体数据量的比例很低。
是否有方法既可以实现上面的优点,同时又可以很好地降低存储呢?
10.3.3 极限存储
- 历史拉链存储
通过新增两个时间戳字段(start_dt和end_dt),将所有以天为粒度的变更数据都记录下来。
(根据状态变更的记录:日期1-状态A、日期2-状态B,生成拉链表)
(类似份额流水表,更新方法为如有新增记录,将上一条旧记录的截止日期改为新纪录的开始日期,再插入新纪录,访问某指定日期数据条件为DATE>=BGNDATE AND DATE<ENDDATE)
存在的问题:
- 对于下游使用方存在一定的理解障碍
解决方法:底层的数据还是历史拉链存储,但是上层做一个视图操作或者在Hive里做一个hook,通过分析语句的语法树,把对极限存储前的表的查询转换成对极限存储表的查询。(查询日期在底层转换为对BGNDATE和ENDDATE的查询)
- 使用BGNDATE和ENDATE进行分区,随着时间的推移分区数量会膨胀
解决方法:分月做历史拉链表,按月分区再按日期分区
在实际生产中,做极限存储需要进行一些额外的处理:
- 做一个全量存储表
全量存储表仅保留最近一段时间的全量分区数据,历史数据通过映射的方式关联到极限存储表。即用户只访问全量存储表,所以对用户来说极限存储是不可见的。
- 对于部分变化频率频繁的字段需要过滤
例如,用户表中存在用户积分字段,这种字段的值每天都在发生变化,如果不过滤的话,极限存储就相当于每个分区存储一份全量数据,起不到节约存储成本的效果。
10.3.4 微型维度
采用极限存储,需要避免维度的过度增长。
比如对于商品维表,每天20多亿条数据,如果在设计商品维度时,将值变化频繁的属性加入到商品维度中,极限情况是每天所有商品数据都发生变化,此时,极限存储没有意义;反之,每天所有商品数据都不发生变化,此时,只需要存储一天的数据即可。
通过将一些属性从维表中移出,放置到全新的维表中,可以解决维度的过度增长导致极限存储效果大打折扣的问题。其中一种解决方法就是上一节提到的垂直拆分,保持主维度的稳定性;另一种解决方式是采用微型维度。
微型维度的创建是通过将一部分不稳定的属性从主维度中移出,并将它们放置到拥有自己代理键的新表中来实现的。这些属性相互之间没有直接关联,不存在自然键。通过为每个组合创建新行的一次性过程来加载数据。
比如淘宝用户维度,用户的注册日期、年龄、性别、身份信息等基本不会发生变化,但用户VIP等级、用户信用评价等级会随着用户的行为不断发生变化。其中VIP等级共有8个值,即-1~6;用户信用评价等级共有18个值。假设基于VIP等级和用户信用评价等级构建微型维度,则在此微型维度中共有8x18个组合,即144条记录,代理键可能是1~144。
图10.3 淘宝交易事实表维度.png微型维度存在以下弊端:
- 局限性
微型维度是事先用所有可能值的组合加载的,需要考虑每个属性的基数,且必须是枚举值。很多属性可能是非枚举型,比如数值类型,如VIP分数、信用分数等;时间类型,如上架时间、下架时间、变更时间等。
- 逻辑复杂
对于分布式系统,生成代理键和使用代理键进行ETL加工都非常复杂,ETL开发和维护成本过高。
- 破坏维度可浏览性
买家维度和微型维度通过事实表建立联系,无法基于VIP等级、信用等级进行浏览和统计。可以通过在买家维度中添加引用微型维度的外键部分来解决此问题,但带来的问题是微型维度未维护历史信息。
10.4 特殊维度
10.4.1 递归层次
递进层次指的是某维度的实例值的层次关系,比如CRM系统客户表记录客户id和父客户id,直到找到顶层客户id,并将客户规模记在顶层客户id下。
维度的递归层次,按照层级是否固定分为均衡层次结构和非均衡层次结构。
在物理实现时,可以使用递归SQL实现,如Oracle中的connectby语句。
在递归层次中进行上钻和下钻是很常见的。由于很多数据仓库系统和商业智能工具不支持递归SQL,且用户使用递归SQL的成本较高,所以在维度模型中,需要对此层次结构进行处理。
- 层次结构扁平化
对于均衡层次结构,采用扁平化最有效。
比如商品类目维度,固定为五级类目,根据记录的层次结构分列出一级五级类目ID,并记录类目级别(15)。如果记录本身为一级类目,则二级~五级类目使用一级类目进行回填,其余层级以此推类。
- 层次桥接表
针对层次结构扁平化所存在的问题,可以采用桥接表的方式来解决,不需要预先知道所属层级,不需要回填,也可解决非均衡层次结构的问题。
图10.5 类目桥接表.png与扁平化方法相比,该方法适合解决更宽泛的分析问题,灵活性好;但加工逻辑复杂,使用逻辑复杂,而且由于事实表和桥接表的多对多关系而带来了双重计算的隐患。
在实际应用中可以根据具体的业务需求来选择技术方案,比如在统计分析或报表中,一般都是按照固定级别进行,如按照一级类目统计GMV、按照省份统计GMV等,现在的扁平化设计完全可以满足需求,而不需要引人复杂的桥接表。很多时候,简单、直接的技术方案却是最好的解决方案。
10.4.2 行为维度
在阿里巴巴的数据仓库中,存在很多维表,如卖家主营类目维度、卖家主营品牌维度、用户常用地址维度等。其中卖家主营类目和主营品牌通过卖家的商品分布和交易分布情况,采用算法计算得到;卖家常用地址通过最近一段时间内物流中卖家的发货地址和买家的收货地址进行统计得到。类似的维度,都和事实相关,如交易、物流等,称之为“行为维度”,或“事实衍生的维度”。
对于行为维度,有两种处理方式,其中一种是将其冗余至现有的维表中,如将卖家信用等级冗余至卖家维表中;另一种是加工成单独的行为维表,如卖家主营类目。
- 避免维度过快增长
比如对商品表进行了极限存储,如果将商品热度加入现有的商品维表中,则可能会使每日商品变更占比过高,从而导致极限存储效果较差。
- 避免稠合度过高
比如卖家主营类目,加工逻辑异常复杂,如果融合进现有的卖家维表中,那么过多的业务稠合会导致卖家维表刷新逻辑复杂、维护性差、产出延迟等。
10.4.3 多值维度
对于多值维度,一种情况是事实表的一条记录在某维表中有多条记录与之对应。
比如对于淘宝交易订单,买家一次购买了多种商品,如一件毛衣和两双袜子,称为交易父订单:对于每种商品的交易,称为交易子订单:此交易父订单有两个子订单与之对应。假设设计交易父订单事实表,则对于此事实表的每一条记录,在商品表中都有一到多条记录与之对应。
- 降低事实表的粒度
在淘宝交易中,前台业务和商业智能关注交易子订单,所以在数据仓库模型设计中,将交易订单设计为子订单粒度,对于每个子订单,只有一种商品与之对应。对于其中的事实,则采用分摊到子订单的方式来解决。但很多时候,事实表的粒度是不能降低的,多值维度的出现是无法避免的。
- 采用多字段方式
比如在房地产销售中,每次合同签订都可能存在多个买受方的情况,如夫妻合买等。对于合同签订事实表,每条记录可能对应多个买受方,而合同已经是此事实中的最细粒度,无法通过降低粒度的方式来解决。由于合同签订的买受人一般不会太多,所以一般采用多字段方式。考虑到扩展性,可以通过预留字段的方式,如超过三个买受方时,其余买受方填写至“其他买受方”字段。
图10.7 采用多字段方式处理多值维度.png- 采用较为通用的桥接表
接表方式更加灵活、扩展性更好,但逻辑复杂、开发和维护成本较高,可能带来双重计算的风险,选择此方式需慎重。通过在事实表和维表之间开发一个分组表,通过此分组表建立连接。
因10.8 采用桥接表方式处理多值维度.png10.4.4 多值属性
维表中的某个属性字段同时有多个值,称之为“多值属性”。它是多值维度的另一种表现形式。
在阿里巴巴的数据仓库中,存在很多维表,如商品SKU维表、商品属性维表、商品标签维表等。每个商品均有一到多个SKU、一到多个属性和一到多个标签,所以商品和SKU、属性、标签都是多对多的关系。
最好的处理方式是维度主键发生变化,一个维度值存放多条记录。比如商品SKU维表,对于每个商品,有多少SKU,就有多少记录,主键是商品的ID和SKU的ID。此种处理方式扩展性好,使用方便,但需要考虑数据的急剧膨胀情况。
10.4.5 杂项维度
杂项维度是由操作型系统中的指示符或者标志字段组合而成的,一般不在一致性维度之列。
比如淘宝交易订单的交易类型字段,包括话费充值、司法拍卖、航旅等类型:支付状态、物流状态等,它们在源系统中直接保存在交易表中。
一个事实表中可能会存在多个类似的字段,如果作为事实存放在事实表中,则会导致事实表占用空间过大;如果单独建立维表,外键关联到事实表,则会出现维度过多的情况;如果将这些字段删除,则会有人不同意。
这时,通常的解决方案就是建立杂项维度,将这些字段建立到一个维表中,在事实表中只需保存一个外键即可。多个字段的不同取值组成一条记录,生成代理键,存入维表中,并将该代理键保存到相应的事实表字段下。建议不要直接使用所有的组合生成完整的杂项维表,在抽取遇到新的组合时生成相应的记录即可。杂项维度的ETL过程比一般的维度略微复杂些。
第11章 事实表设计
11.1 事实表基础
11.1.1 事实表特性
事实表作为数据仓库维度建模的核心,紧紧围绕着业务过程来设计,通过获取描述业务过程的度量来表达业务过程,包含了引用的维度和与业务过程有关的度量。
事实表中一条记录所表达的业务细节程度被称为粒度。通常粒度可以通过两种方式来表述:一种是维度属性组合所表示的细节程度:一种是所表示的具体业务含义。
作为度量业务过程的事实,一般为整型或浮点型的十进制数值,有可加性、半可加性和不可加性三种类型。
维度属性也可以存储到事实表中,这种存储到事实表中的维度列被称为“退化维度”。与其他存储在维表中的维度一样,退化维度也可以用来进行事实表的过滤查询、实现聚合操作等。
事实表有三种类型:
- 事务事实表
用来描述业务过程,跟踪空间或时间上某点的度量事件,保存的是最原子的数据,也称为“原子事实表”。
- 周期快照事实表
以具有规律性的、可预见的时间间隔记录事实,时间间隔如每天、每月、每年等。
- 累积快照事实表
用来表述过程开始和结束之间的关键步骤事件,覆盖过程的整个生命周期,通常具有多个日期字段来记录关键时间点,当过程随着生命周期不断变化时,记录也会随着过程的变化而被修改。
11.1.2 事实表设计原则
-
尽可能包含所有与业务过程相关的事实
-
只选择与业务过程相关的事实
-
分解不可加性事实为可加的组件
-
在选择维度和事实之前必须先声明粒度
-
在同一个事实表中不能有多种不同粒度的事实
-
事实的单位要保持一致
-
对事实的null值要处理
-
使用退化维度提高事实表的易用性
在Kimball的维度建模中,通常按照星形模型的方式来设计,对于维度的获取采用的是通过事实表的外键关联专门的维表的方式,谨慎使用退化维度。
而在大数据领域的事实表设计中,则大量采用退化维度的方式,在事实表中存储各种类型的常用维度信息。这样设计的目的主要是为了减少下游用户使用时关联多个表的操作,直接通过退化维度实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等。通过增加冗余存储的方式减少计算开销,提高使用效率。
1.1.3 事实表设计方法
Kimball对于维度模型设计采用四步设计方法:选择业务过程、声明粒度、确定维度、确定事实。
- 第一步:选择业务过程及确定事实表类型
在明确了业务需求以后,接下来需要进行详细的需求分析,对业务的整个生命周期进行分析,明确关键的业务步骤,从而选择与需求有关的业务过程。
- 声明粒度
粒度的声明是事实表建模非常重要的一步,意味着精确定义事实表的每一行所表示的业务含义,粒度传递的是与事实表度量有关的细节层次。明确的粒度能确保对事实表中行的意思的理解不会产生混淆,保证所有的事实按照同样的细节层次记录。
应该尽量选择最细级别的原子粒度,以确保事实表的应用具有最大的灵活性。同时对于订单过程而言,粒度可以被定义为最细的订单级别。
比如在淘宝订单中有父子订单的概念,即一个子订单对应一种商品,如果拍下了多种商品,则每种商品对应一个子订单:这些子订单一同结算的话,则会生成一个父订单。那么在这个例子中,事实表的粒度应该选择为子订单级别。
- 确定维度
完成粒度声明以后,也就意味着确定了主键,对应的维度组合以及相关的维度字段就可以确定了,应该选择能够描述清楚业务过程所处的环境的维度信息。
比如在淘宝订单付款事务事实表中,粒度为子订单,相关的维度有买家、卖家、商品、收货人信息、业务类型、订单时间等维度。
- 确定事实
事实可以通过回答“过程的度量是什么”来确定。应该选择与业务过程有关的所有事实,且事实的粒度要与所声明的事实表的粒度一致。事实有可加性、半可加性、非可加性三种类型,需要将不可加性事实分解为可加的组件。
比如在淘宝订单付款事务事实表中,同粒度的事实有子订单分摊的支付金额、邮费、优惠金额等。
- 冗余维度
在传统的维度建模的星形模型中,对维度的处理是需要单独存放在专门的维表中的,通过事实表的外键获取维度。这样做的目的是为了减少事实表的维度冗余,从而减少存储消耗。而在大数据的事实表模型设计中,考虑更多的是提高下游用户的使用效率,降低数据获取的复杂性,减少关联的表数量。所以通常事实表中会冗余方便下游用户使用的常用维度,以实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等操作。
比如在淘宝订单付款事务事实表中,通常会冗余大量的常用维度字段,以及商品类目、卖家店铺等维度信息。
11.2 事务事实表
订单作为交易行为的核心载体,直观反映了交易的状况。订单的流转会产生很多业务过程,而下单、支付和成功完结三个业务过程是整个订单的关键节点。获取这三个业务过程的笔数、金额以及转化率是日常数据统计分析的重点,事务事实表设计可以很好地满足这个需求。
11.2.2 单事务事实表
针对每个业务过程设计一个事实表。这样设计的优点不言而喻,可以方便地对每个业务过程进行独立的分析研究。
11.2.3 多事务事实表
将不同的事实放到同一个事实表中,即同一个事实表包含不同的业务过程。
- 不同业务过程的事实使用不同的事实字段进行存放
当不同业务过程的度量差异较大时可以采用,这种方式所存在的问题是度量字段零值较多。
- 不同业务过程的事实使用同一个事实字段进行存放,但增加一个业务过程标签
当不同业务过程的度量比较相似、差异不大时可以采用,但这种方式存在的问题是在同一个周期内会存在多条记录。
11.2.5 父子事实的处理方式
淘宝交易父子订单:在同一个店铺同时下单多种商品,不仅每种商品有一个子订单,而且这几个子订单会再单独产生一个父订单。下单和支付都是在父订单粒度上完成的,比如拍下时的订单总额、支付总额、支付邮费,淘宝交易事务事实表在粒度选择上,按照粒度最细原则,确定为子订单,因此需要将下单总额或者支付总额分摊到每个子订单上,当然只有一个子订单时是不需要进行分摊的。
通过分摊父订单的金额将所有业务过程的度量全部带进淘宝交易事务事实表中,包括下单数量、商品价格、子订单折扣、下单分摊比例、父订单支付金额、父订单支付邮费、父订单折扣、子订单下单金额、子订单下单有效金额、支付分摊比例、子订单支付金额等,将父子事实同时冗余到事务表中。
11.2.6 事实的设计准则
- 事实完整性
事实表包含与其描述的过程有关的所有事实,即尽可能多地获取所有的度量。
在淘宝交易事务事实表中,比如支付业务过程,在子订单粒度上的支付金额、支付邮费、支付红包、支付积分、支付折扣都有所包含,覆盖全面。
- 事实一致性
在确定事务事实表的事实时,明确存储每一个事实以确保度量的一致性。
以淘宝交易事务事实表为例,在下单业务过程中,有下单商品数量和商品价格两个事实,但在事实表中计算了下单金额和下单有效金额,它们可以通过商品数量乘以商品价格进行计算。虽然下游在取数时也可以通过这种方式完成计算,但是在事实表中统一计算可以保证度的一致性,其他如支付过程中的分摊金额等也是类似的。
- 事实可加性
事实表确定事实时,往往会遇到非可加性度量,比如分摊比例、利润率等,虽然它们也是下游分析的关键点,但往往在事务事实表中关注更多的是可加性事实,下游用户在聚合统计时更加方便。
在淘宝交易事务事实表中,存储了分摊比例这样的度量,但更多的是存储各类金额的度量。
11.3 周期快照事实表
在确定的间隔内对实体的度量进行抽样,这样可以很容易地研究实体的度量值,而不需要聚集长期的事务历史。
(截止到今天为止的今年以来交易金额,采样周期为今年)
快照事实表的设计有一些区别于事务事实表设计的性质。
- 用快照采样状态
快照事实表以预定的间隔采样状态度量。这种间隔联合一个或多个维度,将被用来定义快照事实表的粒度,每行都将包含记录所涉及状态的事实。
- 快照粒度
事务事实表的粒度可以通过业务过程中所涉及的细节程度来描述,但快照事实表的粒度通常总是被多维声明,可以简单地理解为快照需要采样的周期以及什么将被采样。
- 密度与稀疏性
快照事实表和事务事实表的一个关键区别在密度上。事务事实表是稀疏的,只有当天发生的业务过程,事实表才会记录该业务过程的事实,如下单、支付等;而快照事实表是稠密的,无论当天是否有业务过程发生,都会记录一行。
- 半可加性
在快照事实表中收集到的状态度量都是半可加的。与事务事实表的可加性事实不同,半可加性事实不能根据时间维度获得有意义的汇总结果。
阿里巴巴数据仓库关于淘宝卖家信用分和DSR快照事实表是直接采用操作型系统数据进行设计加工,采样周期是每天,针对卖家维度的统计,状态度量就是卖家信用分和DSR评分。
注意事项:
- 事务与快照成对设计
数据仓库维度建模时,对于事务事实表和快照事实表往往都是成对设计的,互相补充,以满足更多的下游统计分析需求,特别是在事务事实表的基础上可以加工快照事实表。
如前面所述的淘宝卖家历史至今快照事实表,就是在事务事实表的基础上加工得到的,既丰富了星形模型,又降低了下游分析的成本。
- 附加事实快照事实表
在确定状态度量时,一般都是保存采样周期结束时的状态度量。但是也有分析需求需要关注上一个采样周期结束时的状态度量,而又不愿意多次使用快照事实表,因此一般在设计周期快照事实表时会附加一些上一个采样周期的状态度量。
- 周期到日期度量
在介绍淘宝卖家历史至今快照事实表时,指定了统计周期是卖家历史至今的一些状态度量,比如历史截至当日的下单金额、成交金额等。然而在实际应用中,也有需要关注自然年至今、季度至今、财年至今的一些状态度量,因此在确定周期快照事实表的度量时,也要考虑类似的度量值,以满足更多的统计分析需求。
阿里巴巴数据仓库在设计周期快照事实表时,就针对多种周期到日期的度量设计了不同的快照事实表,比如淘宝卖家财年至今的下单金额、淘宝商品自然年至今的收藏次数等。
11.4 累积快照事实表
针对淘宝交易,设计了淘宝交易下单/支付/确认收货事务事实表,用于统计下单/支付/确认收货的子订单数、GMV等。但仍然有很多需求,此事务事实表很难满足,比如统计买家下单到支付的时长、买家支付到卖家发货的时长、买家从下单到确认收货的时长等。如果使用事务事实表进行统计,则逻辑复杂且性能很差。对于类似于研究事件之间时间间隔的需求,采用累积快照事实表可以很好地解决。
特点:
- 数据不断更新
事务事实表记录事务发生时的状态,对于实体的某一实例不再更新;而累积快照事实表则对实体的某一实例定期更新。
- 多业务过程日期表
累积快照事实表适用于具有较明确起止时间的短生命周期的实体,比如交易订单、物流订单等,对于实体的每一个实例,都会经历从诞生到消亡等一系列步骤。对于商品、用户等具有长生命周期的实体,一般采用周期快照事实表更合适。
累积快照事实表的典型特征是多业务过程日期,用于计算业务过程之间的时间间隔。但结合阿里巴巴数据仓库模型建设的经验,对于累积快照事实表,还有一个重要作用是保存全量数据。
对于淘宝交易,需要保留历史截至当前的所有交易数据,其中一种方式是在ODS层保留和源系统结构完全相同的数据z但由于使用时需要关联维度,较为麻烦,所以在公共明细层需要保留一份全量数据,淘宝交易累积快照事实表就承担了这样的作用一一存放加工后的事实,并将各维度常用属性和订单杂项维度退化到此表中。通常用于数据探查、统计分析、数据挖掘等。
逻辑模型和物理模型密不可分,针对累积快照事实表模型设计,其有不同的实现方式:
- 全量表
此全量表一般为日期分区表,每天的分区存储昨天的全量数据和当天的增量数据合并的结果,保证每条记录的状态最新。此种方式适用于全量数据较少的情况。如果数据量很大,此全量表数据量不断膨胀,存储了大量永远不再更新的历史数据,对ETL和分析统计性能影响较大。
- 全量表的变化形式
此种方式主要针对事实表数据量很大的情况。较短生命周期的业务实体一般从产生到消亡都有一定的时间间隔,可以测算此时间间隔,或者根据商业用户的需求确定一个相对较大的时间间隔。
比如针对交易订单,我们以200天作为订单从产生到消亡的最大间隔。设计最近200天的交易订单累积快照事实表,每天的分区存储最近200天的交易订单;而200天之前的订单则按照gmt_create创建分区存储在归档表中。
此方式存在的一个问题是200天的全量表根据商业需求需要保留多天的分区数据,而由于数据量较大,存储消耗较大。
- 以业务实体的结束时间分区
每天的分区存放当天结束的数据,设计一个时间非常大的分区,比如3000-12-31,存放截至当前未结束的数据。由于每天将当天结束的数据归档至当天分区中,时间非常大的分区数据量不会很大,ETL性能较好;并且无存储的浪费,对于业务实体的某具体实例,在该表的全量数据中唯一。比如对于交易订单,在交易累积快照事实表中唯一。
11.5 三种事实表的比较
表11.7 三种事实表的比较.png- 事务事实表
记录事务层面的事实,用于跟踪业务过程的行为,并支持几种描述行为的事实,保存的是最原子的数据,也称为“原子事实表”。事务事实表中的数据在事务事件发生后产生,数据的粒度通常是每个事务一条记录。一旦事务被提交,事实表数据被插人,数据就不能更改,其更新方式为增量更新。
- 周期快照事实表
以具有规律性的、可预见的时间间隔来记录事实,如余额、库存、层级、温度等,时间间隔为每天、每月、每年等,典型的例子如库存日快照表等。周期快照事实表的日期维度通常记录时间段的终止日,记录的事实是这个时间段内一些聚集事实值或状态度量。事实表的数据一旦插人就不能更改,其更新方式为增量更新。
- 累积快照事实表
被用来跟踪实体的一系列业务过程的进展情况,它通常具有多个日期字段,用于研究业务过程中的里程碑过程的时间间隔。另外,它还会有一个用于指示最后更新日期的附加日期字段。由于事实表中许多日期在首次加载时是不知道的,而且这类事实表在数据加载完成后,可以对其数据进行更新,来补充业务状态变更时的日期信息和事实。
11.6 无事实的事实表
在维度模型中,事实表用事实来度量业务过程,不包含事实或度的事实表称为“无事实的事实表。虽然没有明确的事实,但可以用来支持业务过程的度量。
常见的无事实的事实表主要有如下两种:
- 事件类
记录事件的发生。
在阿里巴巴数据仓库中,最常见的是日志类事实表。比如用户的浏览日志,某会员某时间点浏览了淘宝首页、某会员某时间点浏览了某卖家的店铺中的某商品详情页等。对于每次点击,其事实为1,但一般不会保存此事实。
- 条件、范围或资格类
记录维度与维度多对多之间的关系。比如客户和销售人员的分配情况、产品的促销范围等。
11.7 聚集型事实表
数据仓库的性能是数据仓库建设是否成功的重要标准之一。聚集主要是通过汇总明细粒度数据来获得改进查询性能的效果。通过访问聚集数据,可以减少数据库在响应查询时必须执行的工作量,能够快速响应用户的查询,同时有利于减少不同用户访问明细数据带来的结果不一致问题。尽管聚集能带来良好的收益,但需要事先对其进行加载和维护,这将会对给ETL带来更多的挑战。
阿里巴巴将使用频繁的公用数据,通过聚集进行沉淀,比如卖家最近1天的交易汇总表、卖家最近N天的交易汇总表、卖家自然年交易汇总表等。这类聚集汇总数据,被叫作“公共汇总层”。
聚集的基本步骤:
- 确定聚集维度
在原始明细模型中会存在多个描述事实的维度,如日期、商品类别、卖家等,这时候需要确定根据什么维度聚集,如果只关心商品的交易额情况,那么就可以根据商品维度聚集数据。
- 确定一致性上钻
这时候要关心是按月汇总还是按天汇总,是按照商品汇总还是按照类目汇总,如果按照类目汇总,还需要关心是按照大类汇总还是小类汇总。
- 确定聚集事实
在原始明细模型中可能会有多个事实的度量,比如在交易中有交易额、交易数量等,这时候要明确是按照交易额汇总还是按照成交数量汇总。
基本原则:
- 一致性
聚集表必须提供与查询明细粒度数据一致的查询结果。从设计角度来看,确保一致性,最简单的方法是确保聚集星形模型中的维度和度量与原始模型中的维度和度量保持一致。
- 避免单一表设计
不要在同一个表中存储不同层次的聚集数据;否则将会导致双重计算或出现更糟糕的事情。在聚集表中有些行存放按天汇总的交易额,有些行存放按月汇总的交易额,这将会让使用者产生误用导致重复计算。
为了避免此类问题,通用的做法是在聚集时显式地加入数据层级列以示区别,但是这样会加大使用者的使用成本。行之有效的另一种方法是把按天与按月汇总的交易额用两列存放,但是需要在列名或者列注释上能分辨出来。
- 聚集粒度可不同
聚集并不需要保持与原始明细粒度数据一样的粒度,聚集只关心所需要查询的维度。
订单涉及的维度有商品、买家、卖家、地域等,比如可以按照商品汇总一天的交易额,可以按照卖家汇总一天的营业额(交易额),可以按照商品与地域汇总一月的交易额。
实践原则:
- 数据公用性
汇总的聚集会有第三者使用吗?基于某个维度的聚集是不是经常用于数据分析中?如果答案是肯定的,那么就有必要把明细数据经过汇总沉淀到聚集表中。
- 不跨数据域
数据域是在较高层次上对数据进行分类聚集的抽象。阿里巴巴以业务过程进行分类,如交易统一划到交易域下,商品的新增、修改放到商品域下。
- 区分统计周期
在表的命名上要能说明数据的统计周期,如_1d表示最近1天,_td表示截至当天,_nd表示最近N天。
补充说明:
- 聚集是不跨越事实的
聚集是针对原始星形模型进行的汇总,为了获取和查询与原始模型一致的结果,聚集的维度和度量必须与原始模型保持一致,因此聚集是不跨越事实的。
横向钻取是针对多个事实基于一致性维度进行的分析,很多时候采用融合事实表,预先存放横向钻取的结果,从而提高查询性能。因此,融合事实表是一种导出模式而不是聚集。
- 聚集带来的问题
聚集会带来查询性能的提升,但聚集也会增加ETL维护的难度。当子类目对应的一级类目发生变更时,先前存在的、已经被汇总到聚集表中的数据需要被重新调整。这一额外工作随着业务复杂性的增加,会导致多数ETL人员选择简单强力的方法,删除并重新聚集数据。
交易汇总表设计:
聚集是指针对原始明细粒度的数据进行汇总。
(以淘宝交易事务事实表为例,图略)
交易订单明细:事实和商品、卖家、买家等维度关联
- 按商品粒度汇总(最近1天商品粒度汇总表)
聚集维度:商品
一致性上钻:按商品(商品ID)最近1天汇总
聚集事实:下单量、交易额
- 按卖家粒度汇总(最近N天卖家粒度汇总表)
聚集维度:卖家
一致性上钻:按卖家(卖家ID)最近7天和最近30天汇总(分别用两列存放)
聚集事实:交易额
- 按卖家、买家、商品粒度汇总
确定聚集维度:卖家、买家、商品
确定一致性上钻:按卖家(卖家ID)、买家(买家ID)、商品(商品ID)最近1天汇总
确定聚集事实:交易额
可以看出聚集的粒度越细,记录的条数越多,就会越接近原始明细模型的粒度。
- 按二级类目汇总
聚集维度:类目
一致性上钻:按最近1天类目维度的二级维度属性汇总
聚集事实:交易额