一、基础概念
重点掌握:
- 数据仓库的特点
- 数据仓库与数据库的练习与区别
- 为什么要使用数据仓库 / 使用数据仓库的目的是什么
1. 数据仓库的起源
1.1 数据仓库的概念
数据仓库之父比尔·恩门(Bill Inmon)在 1991 年出版的“Building the Data
Warehouse”一书中所提出的定义被广泛接受:数据仓库(Data Warehouse)
是一个面向主题的(Subject Oriented)、集成的(Integrated)、相对稳定的
(Non-Volatile)、反映历史变化(Time Variant)的数据集合,数据仓库的目
标是用于支持分析和决策(Decision Making Support)。
它是一个过程 ,一个解决方案,一套方法论,不是一个具体产品。
1.2 数据仓库的特点
(☆☆☆重点掌握)
面向主题性
数据仓库主要面向分析主题,因此按照主题来组织,而不是按照应用程序或者业务流程组织数据,主题是用户关心的一类业务或问题。例如:销售、客户、产品等。举例说明:数据仓库被设计成辅助人们分析数据。例如,一个公司要分析销售数据,就可以建立一个专注于销售的数据仓库,使用这个数据仓库,就可以回答类似于“去年谁是我们这款产品的最佳用户”这样的问题。这个场景下的销售,就是一个数据主题,而这种通过划分主题定义数据仓库的能力,就使得数据仓库是面向主题的。主题域是对某个主题进行分析后确定的主题的边界,客户、销售、产品都是主题域的例子。
集成性
数据仓库集成来自不同系统和数据源的数据,将其统一的存储和管理,消除数据冗余和不一致性。举例说明:集成的概念与面向主题是密切相关的。还用销售的例子,假设公司有多条产品线和多种产品销售渠道,而每个产品线都有自己独立的销售数据库,此时要从公司层面整体分析销售数据,必须将多个分散的数据源进行抽取并统一成一致的,无歧义的数据格式后,再放置到数据仓库中。因此数据仓库必须能够解决诸如产品命名冲突、码值统一、计量单位不一致等问题。当完成了这些数据整合工作后,该数据仓库就可称为是集成的。
相对稳定性
数据仓库的数据是稳定的、可靠的,不会频繁变化,以保证决策分析的准确性和一致性。举例说明:OLTP 型数据库中的数据通常实时更新,数据根据需要及时发生变化。数据仓库的数据主要供企业决策分析之用,所涉及的数据操作主要是数据查询,一旦某个数据进入数据仓库以后,一般情况下将被长期保留,也就是
数据仓库中一般有大量的查询操作,但修改和删除操作很少,通常只需要定期的加载、刷新
。反映历史变化
数据仓库是反映历史变化的,OLTP 型数据库主要关心当前某一个时间段内的数据,而数据仓库中的数据通常包含历史信息,系统记录了企业从过去某一时点(如开始应用数据仓库的时点)到目前的各个阶段的信息,通过这些信息,可以对企业的发展历程和未来趋势做出定量分析和预测。
当然,任何一个存储结构都不可能无限扩展。当数据超过数据仓库的存储期限,或对分析无用时,从数据仓库中删除这些数据。
除了以上四个特性外,数据仓库还有一个非常重要的概念就是粒度。粒度问题遍布于数据仓库体系结构的各个部分。粒度是指数据的细节或汇总程度,细节程度越高,粒度级别越细。这里举个例子:
(1)例如,销售库里的销售额, 可以是一天一个值, 也可以是一个月一个值,甚至一年一个值, 这就是相对于时间维度表的粒度。
粒度也不是越细越好, 也要综合分析。数据粒度一直是数据仓库设计需要重点考虑的问题。
粒度之所以是数据仓库环境的关键设计问题,是因为它极大地影的的数据仓库的数据量和可以进行的查询类型,粒度级别越低,数据量越大,查询的细节程度越高。查询范围越广泛,反之亦然。
1.3 为什么要有数据仓库
通常数据仓库的数据来自各个业务应用系统。业务系统中的数据形式各种各样,可能是 oracle、mysql 等关系数据库的结构化数据,可能是文本、CSV 等文件或Word、Excel 文档中的非结构化数据,还可能是 HTML、XML 等自描述的半结构化数据。这些业务数据经过一系列的数据抽取、转换、清洗,最终以一种统一的格式装载进数据仓库。
从存储的角度看,数据仓库里的数据实际上已经存在于业务应用系统中,那么为什么不能直接操作业务系统中的数据用于分析,而要使用数据仓库呢?
实际上在数据仓库技术出现前,有很多数据分析的先驱者已经发现,简单的“直接访问”方式很难良好工作,这样做的失败案例数不胜数。下面列举一些直接访问业务系统无法工作的原因:
- 某些业务数据由于安全或其他因素不能直接访问。
- 业务系统的版本变更很频繁,每次变更都需要重写分析系统并重新测试。
- 很难建立和维护汇总数据来源于多个业务系统版本的报表。
- 业务系统的列名通常是硬编码,有时仅仅是无意义的字符串,这让编写分析系统更加困难。
- 业务系统的数据格式,如日期、数字的格式不统一。
- 业务系统的表结构为事务处理性能而优化,有时并不适合查询与分析,没有适当的方式将有价值的数据合并进特定应用的数据库。
- 没有适当的位置存储元数据。
- 用户需要看到的显示数据字段,有时在数据库中并不存在。
- 通常事务处理的优先级比分析系统高,所以如果分析系统和事务处理运行在同一硬件之上,分析系统往往性能很差。
- 极有可能影响业务系统的性能。
数据仓库(开源的 Hadoop 体系和阿里的 MaxCompute/ODPS 等)本质上也
是数据库,只不过它主要用于:
- 从外部数据源引入和存储历史数据;
- 分析数据。
因此相比事务性数据,数据仓库对数据集成和数据分析能力要求较高,相对应的功能更丰富和更强大。比如对复杂的 JSON 文本处理和分析, 可以通过集成 Java / Python/Shell 等语言自定义 UDF 函数来实现更加灵活的处理。
下面简单总结一下什么时候需要开始使用数据仓库:
- 需要分析的数据量较大(单批 GiB),此时事务性数据库分析性能堪忧,需要通过建立索引而且会分析查询会影响在线事务。而数仓一般采用列式存储,自带索引性能加成。
- 分析查询不想影响在线事务
- 需要记录历史数据
- 需要对来自不同数据库、数据源的数据进行整合、关联分析和交叉探查。比如将存储在 SLS 的用户日志和 MySQL 的用户信息进行关联分析,通过用户的行为日志建立用户画像。
- 批处理分析,需要定期批量查询分析数据并生成结果,比如各种 BI 报表。
1.4 数据仓库 VS 数据库(☆☆理解记住)
(1)数据库
数据库是面向业务的处理系统,它是针对具体业务在数据库联机的日常操作,通常对记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作型处理,也被称为联机事务处理 OLTP(On-Line Transaction Processing)。
(2)数据仓库
数据仓库是面向主题的处理系统,一般针对某些主题的历史数据进行分析,支持管理决策,又被称为联机分析处理 OLAP(On-Line Analytical Processing)。
(3) 两者区别
首先要明白,数据仓库的出现,并不是要取代数据库。数据仓库中的数据面向主题、数据集成、不易更新、反映历史变化的特点,这些特点是数据库系统没有的,两者区别主要包括:面向需求不同、系统设计目标不同、设计方法不同等详情见下表。
举个例子:
以银行业务为例。数据库是事务系统的数据平台,客户在银行做的每笔交易都会写入数据库,被记录下来,这里,可以简单地理解为用数据库记账。
数据仓库是分析系统的数据平台,它从业务系统获取数据,并做汇总、加工,为决策者提供决策的依据。比如,某银行某分行一个月发生多少交易,该分行当前存款余额是多少。如果存款又多,消费交易又多,那么该地区就有必要设立ATM 了。
显然,银行的交易量是巨大的,通常以百万甚至千万次来计算。业务系统是实时的,这就要求时效性,客户存一笔钱需要几十秒是无法忍受的,这就要求数据库只能存储很短一段时间的数据。而分析系统是事后的,它要提供关注时间段内所有的有效据。这些数据是海量的,汇总计算起来也要慢一些,但是只要能够提供有效的分析数据就达到目的了。
数据仓库,是在数据库已经大量存在的情况下,为了进一步挖掘数据资源、为了决策需要而产生的,它决不是所谓的“大型数据库”。
1.5 数据仓库 VS 数据湖
随着大数据时代的发展,环境发生了变化,使得数据仓库这种先处理后使用的方式成本激增。
首先,数据量发生了显著的增加,对数据仓库的存储是一个巨大的挑战;
其次,数据结构也非常多元化,不再都是结构化数据,而是出现了半结构化数据,甚至语音视频等非结构数据。但是人们同时意识到数据包含巨大的潜在价值,对于很多数据,即使暂时没有明确的需求,也想先留一份。
所以,妥协的办法出现了,从先处理后用,变成了先存储,以后用的时候再考虑怎么处理。这就是数据湖的处理方式。这样做有两个好处,一个是保留了原始数据的原汁原味,这样可以避免任何前期的数据加工造成的数据变形。另外一个好处就是节约了前期数据接入时的成本。
数据湖是一个以原始格式(通常是对象块或文件)存储数据的系统或存储库。数据湖通常是所有企业数据的单一存储。用于报告、可视化、高级分析和机器学习等任务。数据湖可以包括来自关系数据库的结构化数据(行和列)、半结构化数据(CSV、日志、XML、JSON)、非结构化数据(电子邮件、文档、pdf)和二进制数据(图像、音频、视频)。
最初构思数据湖时,人们认为只需要提取数据并将其放入数据湖即可。一旦进入数据湖,终端用户就可以一头扎进去,找到数据并进行分析。然而,企业很快发现,使用数据湖中的数据与仅仅将数据放入湖中完全是两码事。
当前,业界出现了一种称为 data lakehouse(湖仓一体) 的新型数据架构,它通过一种新的开放和标准化的系统设计来实现:其实现与数据仓库中类似的数据结构和数据管理功能,并将数据存放在数据湖使用的低成本存储系统中。
目前作为前沿的大数据架构,数据湖和数据仓库一体正蓬勃发展,有望成为新一代的大数据应用架构。
2. 数据模型
2.1 什么是数据模型
数据模型就是数据组织和存储方法,是数据特征的抽象,通过抽象的实体及实体之间联系的形式,来表示现实世界中事物的相互关系的一种映射,也是对数据背后业务的抽象表达,可以用一个公式表示其结构:
数据模型 = 「数据组件 + 组件间关系 + 规约」
- 数据组件指各类业务对象或实体,对应到数据模型的图形化表达上,一般是
「点」,常见的数据组件有业务对象(如:用户、商家)、表(如:Hive 物
理表)、指标等; - 组件间的关系就是多个数据组件之间的各种关系,对应到数据模型的图形化
表达上,一般是「边」,常见的组件间的关系有关联关系(如用户与商品之
间,商品与商家之间)、泛化关系(如:用户与支付宝用户,商家与小程序
商家); - 规约指各类组件及关系的约束条件,如数据组件的命名约束、关联关系的方
向约束、主外键约束等等。
数据模型表现和抽象的是实体和实体之间的关系,通过对实体和实体之间关系的定义和描述,来表达实际的业务中具体的业务关系。
数据仓库模型是数据模型中针对特定的数据仓库应用系统的一种特定的数据模型,从层级上看,依次分为:概念模型、逻辑模型、物理模型:
- 概念模型:是一种面向用户、面向客观世界的模型,用抽象的概念名词来抽象表达业务,按数据模型的结构,概念模型对应的数据组件一般是各类数据实体,如:业务对象(用户、商家、商品…),业务活动(交易、投保、配送…)。
- 逻辑模型:逻辑模型既面向用户,又面向物理系统,它是在概念模型的基础上,扩展概念的属性实现的,如用户这个概念模型数据组件,扩展性别、年龄、住址等等属性,形成一个表格,逻辑模型的载体可以是一个文档,也可以是一张虚拟表。
- 物理模型:主要是根据逻辑模型,基于物理系统的特性进行针对性的适配,如:分区、存储结构、索引策略、冗余等等,载体一般是物理表。
2.2 为什么需要数据模型
大数据系统需要数据模型方法来帮助更好地组织和存储数据,以便在性能、成本、效率和质量之间取得最佳平衡。
- 性能:良好的数据模型能帮助我们快速查询所需要的数据,减少数据的 I/O 吞吐。
- 成本:良好的数据模型能极大地减少不必要的数据冗余,也能实现计算结果复用,极大地降低大数据系统中的存储和计算成本。
- 效率:良好的数据模型能极大地改善用户使用数据的体验,提高使用数据的效率。
- 质量:良好的数据模型能改善数据统计口径的不一致性,减少数据计算错误的可能性。
2.3 如何建设数据模型
建设数据模型既然是整个数据仓库建设中一个非常重要的关键部分,那么,怎么建设我们的数据仓库模型就是我们需要解决的一个问题。
这里我们将要以供应链业务介绍如何创建适合自己的数据模型。概念模型、逻辑模型、物理模型之间的关系如下图所示:
数据仓库的数据建模大致分为三个阶段:
- 概念建模,这部分的建模工作侧重业务分析,主要包含以下几个部分:
- 理清业务活动并抽象化;
- 分析确认业务活动中参与业务实体;
- 分析确认业务实体之间的关系;
- 逻辑建模,这部分的建模工作侧重实体关系设计,主要包含以下几个部分:
- 业务实体化的属性和值域;
- 梳理实体的属性和实体之间的联系;
- 物理建模,这部分的建模工作维度模型设计,主要包含以下几个部分:
- 综合考虑存储和查询性能,进行数据仓库模型设计,维度表和事实表的设计
(不展开);
- 综合考虑存储和查询性能,进行数据仓库模型设计,维度表和事实表的设计
最终梳理出核心的业务流程图:
首先识别业务过程。识别业务过程之前要先找出业务中的实体。对于供应链体系来说主要的实体就是:供应商、商品、经销商、用户、仓库、资金等。把上边的业务流图稍作简化,从头走一遍。经销商先向供应商采购。供应商发货给仓库入库。仓库理货,上架商品进行售卖。用户购买商品,然后仓库发货出库。物流快递派送商品到用户。用户确认收货。经销商和供应商进行财务结算。整个流程走完,基本重要业务过程节点都清晰了。把这些关键节点的业务过程再进行细化和完善。比如采购之前,需要签合同,入库之前需要预约等。
业务过程梳理清晰后,还要根据实际的业务判断,哪些数据是获取不到的,哪些是业务部关注的。对业务过程进行精简,精简后就可以对核心的数据进行物理建模了。
- 实体通常以 DIM 维表形式构建。比如:供应商维表、货品维表、货品商品关系维表等。
- 业务过程以 DWD 事实表形式构建。比如:采购单流水表、入库流水表、出库流水表等。
从我们上面对数据仓库的数据建模阶段的各个阶段的划分,我们能够了解到整个数据仓库建模的主要工作和工作量,能够对我们在实际的项目建设能够有所帮助。
二、数据仓库的架构
重点掌握:
- 请讲一下常见的数据仓库架构有哪些?设计思路是什么样的?
- 请讲一下 Inmon、Kimball 两种数仓架构的特点。
- 请讲一下你们公司当前数据建设的情况、所处的阶段、存在什么样的问题。
1. 基本架构
“架构”是什么? 架构的定义,一般指的是整体结构和结构组件之间的关系。数据仓库的架构,说的就是常见的数据仓库的结构以及内部结构之间的关系。数据仓库系统一般采取下图架构来满足上述使用场景的:
- 数据从不同的数据源引入到统一的存储
- 在统一存储里进行分析计算
- 计算得到分析结果给外部消费
- 串 联 上 述 数 据 引 入 、 处 理 和 分 析 过 程 的 程 序 就 是 ETL(Extract-Transform-Load,抽取-转换-加载)任务
2. 主要数据仓库架构(☆☆理解记住)
2.1 离线架构
2.1.1 数据分层架构
搞过数据仓库的,都知道所谓的三层架构,那么数据为何要做分层呢?
- 清晰数据结构:每一个数据分层都有它的作用,这样我们在使用表的时候能更方便地定位和理解。
- 减少重复开发:规范数据分层,开发一些通用的中间层数据,能够减少极大的重复计算。
- 复杂问题简单化:将一个复杂的任务分解成多个步骤来完成,每一层只处理单一的步骤,比较简单和容易理解。而且便于维护数据的准确性,当数据出现问题之后,可以不用修复所有的数据,只需要从有问题的步骤开始修复。
- 屏蔽原始数据的异常:屏蔽业务的影响,不必改一次业务就需要重新接入数据。
一般数据仓库的三层架构,典型如阿里的 ONEDATA 体系,就是按照三层架构来落地。
当然在各个层次之下还可以有细化层次或者是减少层次,比如:
- 针对新创建业务场景,其看数思路和指标体系并不固定的情况下,多数情况下公共层只会构建 DWD 等明细层,弱化 DWS;
- 在应用层内部,可能因为结构清晰和数据加工计算方便等考量,会在细化抽象层次,比如之前很多人提到的公共应用层或者是所谓接口服务层等概念。
架构这个东西,存在即是合理,只要合理的考虑层次的用途,基于各自的业务和数据现状,可以针对性的细化分层架构。
2.1.2 Inmon 数据仓库架构(了解)
范式建模,并且很重视数据清洗。
Inmon 企业信息工厂架构如下图所示,我们来看一下图中组件是如何协同工作的。
- 应用系统,这些应用是组织中的操作型系统,用来支撑业务。它们收集业务处理过程中产生的销售、市场、材料、物流等数据,并将数据以各种形式进行存储。操作型系统也叫源系统,为数据仓库提供数据。
- ETL 过程,ETL 过程从操作型系统抽取据,然后将数据转换成一种标准形式,最终将转换后的数据装载到企业级数据仓库中。ETL 是周期性运行的批处理过程。
- 企业级数据仓库,是该架构中的核心组件。正如 Inmon 数据仓库所定义的,企业级数据仓库是一个细节数据的集成资源库。其中的数据以最低粒度级别被捕获,存储在满足三范式设计的关系数据库中。
- 部门级数据集市,是面向主题数据的部门级视图。数据从企业级数据仓库取。数据在进入部门数据集市时可能进行聚合。数据集市使用多维模型设计,用于数据分析。重要的一点是,所有的报表工具、BI 工具或其他数据分析应用都从数据集市查询数据,而不是直接查询企业级数据仓库。
2.1.3 Kimball 数据仓库架构(目前主流的架构)
Kimball 数据仓库架构如图所示:
对比上一张图可以看到,Kimball 与 lnmon 两种架构的主要区别在于核心数据仓库的设计和建立。Kimball 的数据仓库包含高粒度的企业数据,使用多维模型设计,这也意味着数据仓库由星型模式的维度表和事实表构成。分析系统或报表工具可以直接访问多维数据仓库里的数据。在此架构中的数据集市也与 lnmon中的不同。这里的数据集市是一个逻辑概念,只是多维数据仓库中的主题域划分,并没有自己的物理存储,也可以说是虚拟的数据集市。
2.1.4 混合型数据仓库架构
混合型数据仓库架构如图所示:
所谓的混合型结构,指的是在一个数据仓库环境中,联合使用 Inmon 和Kimball 两种架构。从架构图可以看到,这种架构将 Inmon 方法中的数据集市部分替换成了一个多维数据仓库,而数据集市则是多维数据仓库上的逻辑视图。使用这种架构的好处是,既可以利用规范化设计消除数据冗余,保证数据的粒度足够细;又可以利用多维结构更灵活地在企业级实现报表和分析。
2.2 实时架构
2.2.1 Lambda 架构
随着大数据应用的发展,人们逐渐对系统的实时性提出了要求,为了计算一些实时指标,就在原来离线数仓的基础上增加了一个实时计算的链路,并对数据源做流式改造(即把数据发送到消息队列),实时计算去订阅消息队列,直接完成指标增量的计算,推送到下游的数据服务中去,由数据服务层完成离线 & 实时结果的合并,以此实现对离线产生的数据和实时产生的数据进行查询。
2.2.2 Kappa 架构
Kappa 架构可以认为是 Lambda 架构的简化版(移除 lambda 架构中的批处理部分)。
在 Kappa 架构中,正常情况下 kafka 都是吐出实时数据,经过实时处理系统,进入服务数据库,当需要数据订正或历史数据重新处理时都通过上游重放消息完成。
Kappa 架构最大的问题是流式重新处理历史数据的吞吐能力会低于批处理,但这个可以通过增加计算资源来弥补。
Kappa 架构问题:
- 消息中间件缓存的数据量和回溯数据有性能瓶颈。通常算法需要过去 180 天的数据,如果都存在消息中间件,无疑有非常大的压力。同时,一次性回溯订正180 天级别的数据,对实时计算的资源消耗也非常大。
- 在实时数据处理时,遇到大量不同的实时流进行关联时,非常依赖实时计算系统的能力,很可能因为数据流先后顺序问题,导致数据关联不上而被误抛弃。
2.2.3 HSAP 架构
如下图所示是阿里巴巴在 2011~2024 年间沉淀下来的一套实时数仓架构,其本质上也是 Lambda 架构,通过一系列存储和计算引擎(HBase、Druid、Hive、Drill、Redis 等)的复杂配合才能满足业务的需求,并且多个存储之间需要通过数据同步任务来保持大致的同步。这种业务架构极其复杂,整个业务的开发耗费了大量的时间。然而随着业务量的增长,随着数据的增长,关系复杂度越来越大,成本急剧增加,因此,我们迫切的需要一种更优雅的方案去解决类似的问题。
基于上述背景,提出了 HSAP 理念,HSAP 是指 Hybrid serving and analytical processing(混合型服务分析一体化),理念是既能支持很高 QPS 场景的实时写入,又能将复杂的分析场景在一套体系里面完成。HSAP 相当于数仓 + 在线(OLAP + OLTP)服务,是两者的超集。
那么 HSAP 理念落地的其核心是什么呢?
- 首先,要有一套非常强大的存储,能够同时存储实时数据和离线数据,统一数据存储;
- 同时还要有一种高效的查询服务,在同一个接口下(比如 SQL),能够支持高 QPS 的查询,支持复杂的分析以及联邦查询和分析;
- 系统能够直接对接前端应用,例如报表和在线服务等,不需要再额外的导入导出就能即席分析,统一数据服务,减少数据移动。
HSAP 理念的产品,阿里云 Hologres。用户、商品、商家属性数据和海量的用户行为数据经过实时和离线的数据清洗统一进入 HSAP 系统,由 HSAP 系统向上承接了实时大屏、实时报表、效果跟踪、实时数据应用等查询和分析服务。
3.数仓建模方法(☆☆理解记住)
如程序语言设计大师 Niklaus Wirth 所说,程序=算法+数据结构。数据仓库这一大程序中关注最重要的 2 件事情就是:
- 数据仓库的数据结构:如何设计数据的存储格式和数据之间的关联关系?也就是数据建模。
- 数据仓库的算法:如何编写和调度数据处理任务,完成数据的引入、处理分析算法。也就是 ETL 任务。
3.1 Inmon 模式(范式建模)
3.1.1 概念
Inmon 模式从流程上看是自上向下的,即从上游业务系统的数据着手, 进而构建出整个企业共享的数据仓库, 在这个数据仓库下延伸出不同主题的数据集市(数据集市就是关于某一个主题的小数据仓库),比如账单数据作为一个账单数据集市。(先有数据仓库再有数据集市)一种瀑布流开发方法。
3.1.2 流程
通常,Inmon 都是以数据源头为导向。首先,需要探索性地去获取尽量符合预期的数据,尝试将数据按照预期划分为不同的表需求。其次,明确数据的清洗规则后将各个任务通过 ETL 由 Stage 层转化到 DW 层,将数据抽象为实体-关系模型。接着,在完成 DW 的数据治理之后,可以将数据输出到数据集市中做基本的数据组合。最后,将数据集市中的数据输出到 BI 系统中去辅助具体业务。
3.1.3 特点
在这种场景下,数据无法从 stage 层直接输出到 dm 层,必须先通过 ETL 将数据的格式清洗后放入 dw 层,再从 dw 层选择需要的数据组合输出到 dm 层。在 Inmon 模式中,并不强调事实表和维度表的概念,因为数据源变化的可能性较大,需要更加强调数据的清洗工作,从中抽取实体-关系。
3.2 Kimball 模式(维度建模)
3.2.1 概念
Kimball 模式从流程上看是自下向上的,是指从最下游的业务数据分析需求开始,先想清楚要解决什么问题,解决这些问题需要知道哪些业务流程、索要分析的数据的维度和指标度量需求,再去上游数据源捞取需要的数据。根据分析的领域不同,逐渐积累出一堆数据集市。这堆逐渐构建出来的数据集市就是逻辑上的数据仓库。即从数据集市->数据仓库->数据源(先有数据集市再有数据仓库)的一种敏捷开发方法。
3.2.2 流程
通常,Kimball 都是以最终任务为导向。首先,在得到数据后需要先做数据的探索,尝试将数据按照目标先拆分出不同的表需求。其次,在明确数据依赖后将各个任务再通过 ETL 由 Stage 层转化到 DM 层。这里 DM 层数据则由若干个事实表和维度表组成。接着,在完成 DM 层的事实表维度表拆分后,数据集市一方面可以直接向 BI 环节输出数据了,另一方面可以先 DW 层输出数据,方便后续的多维分析。
3.2.3 特点
Kimball 支持数据仓库总线结构,提倡维度建模,以星型模型或是雪花模型等方式构建维度数据仓库。架构体系中,数据集市与维度数据仓库是紧密结合的,数据集市是数据仓库中一个逻辑上的主题域。各种前端工具将可以直接访问使用维度数据仓库。
Kimball 往往意味着快速交付、敏捷迭代,不会对数据仓库架构做过多复杂的设计,在变幻莫测的互联网行业,这种架构方式逐渐成为一种主流范式。
3.3 Inmon VS Kimball 建模方式对比
3.3.1 架构设计
Inmon 架构的思想在于自上而下的构建数据模型。以数据源头开始,数据以瀑布流式的向下流动,更加注重数据清洗。其核心思想在于数仓分层。(换言之就是,尽量将某一主题域的所有数据都考虑到数据建模中。这样设计的数据模型的覆盖面会更广,但是数据模型的设计会困难)。
Kimball 架构的思想在于自下而上的构建数据模型。以具体业务诉求为出发点,设计数据模型,其核心思想在于维度建模。(即根据业务需求找到关注的数据指标,基于关注的数据指标设计数据模型,这样设计的数据模型会更加的独立,但可能会导致数据模型过多发展)。
目前企业的数据仓库设计一般都是综合了两者的实现,整体上采用分层架构,具体的模型设计上采用维度建模。
3.3.2 实现特点
由于两派构建数据仓库的根本理念上就存在着差异,所以他们的数据建模方法也不同。
Inmon 偏向于构建一个企业级别中央大一统的数据仓库,所以 Inmon 推崇使用== ER 模型建立关系模型并坚持使用3NF 规范==化进行数据模型的建设,这样的好处又很多:数据一致性好,一处改动多处一致、减少数据冗余等等。这种方式对业务稳定的企业来说是可行的,但这样设计和开发效率较低,不够敏捷,不适合业务发展迅猛的企业,尤其是互联网行业;
Kimball 建议不要一上来就想搞大工程,要以需要分析的问题和业务为导向,小步快跑地集成数据来解决一个个具体的业务问题,并逐渐积累为数据仓库,这种小步快跑的迭代方法更敏捷,更受互联网公司青睐。它以分析问题为导向,因此重点关注业务问题分析中需要什么「维度属性」以及「事实度量值」,比如分析销售额时会重点关注区域、城市、日期、商品类别等维度,以及销售额、成本、利润等事实度量值。因此 KimBall 提出的「维度建模」方法以构建「维度表」和「事实表」为核心,并且为了让分析师更高效使用数据表,减少表之间的 Join 关联操作,因而放弃 3NF 规划化,提倡反规范化,构建大量宽表。当然缺点就是数据大量冗余、数据不一致,而且容易重复建设。
说了那么多,其实核心观点就是:不同的企业业务类型就应该用不同的数据建模方法。
在互联网行业中,需求变化飞快,追求敏捷更符合实际场景,阿里 MaxCompute 也推荐使用维度建模,数据建模工具的域名就是:https://kimball.alibaba-inc.com。除此以外,微软的 Power BI、Tableau 等业界 BI数据工具都可以采用维度建模。
我们可以拿几个例子来解释说明一下。
- 营销:这是一个专业领域,我们不需要为了分析的目的考虑营销的每个方面。因此,我们不需要企业仓库——几个数据集市就足够了——也就是 Kimball方法。
- 广告:这是一个小的分支,并且业务场景较为简单,无需进行企业级数仓建设,只需要数据集市就够了。因此,Kimball 的方法比较适合。
- 保险:为了根据未来的预测管理风险,我们需要对所有投保人形成一个广泛的图景,由一系列数据组成,如盈利能力、历史、人口统计等。所有这些方面都是相互关联的,因此 Inmon 方法比较合适。
- 银行:银行类的业务对于银行产品和客户信息都是非常关注,尤其是两者的交叉分析,哪些人买了啥银行产品。这些数据会有相关的限制,例如:产品和客户的信息不可给市场和财务部门公开,部门与部门之间的数据会有限制,这种情况下只能采用 Kimball 的方法;如果银行中的整个流程和部门相互关联,这种情况下使用 Inmon 会更好一些。
- 制造&电信:会涉及到多个组织单元,且预算比较充裕。这种情况没有系统依赖,因而需要企业模型,这时还是 Inmon 的方法比较理想。
4. 阿里数仓架构升级之路
大型公司的数据建设也不是一蹴而就的,其实也是一步步地迭代建设的。在数仓架构层面,它通过多轮的迭代建设使其变得更加科学。
4.1 数仓架构升级方向
随着数据应用场景的扩大,如数据化运营、算法策略、产品优化等场景的出现,在多场景协同落地中出现了问题。在近 10 年的实践迭代中,整体数据架构发现了以下问题:
- 架构的定位与边界不清晰,导致 ADS 烟囱化开发:此前 OneData 定义了 CDM和 ODS 的范围,但缺少对 ADS 的范围定义,存在 ADS 与 DWS、ODS 的边界不清晰,导致 ADS 自建轻度汇总和 ODS 明细、竖立小烟囱等问题。如数据团队存在
生意参谋、数据银行等多个数据应用团队及数据公共层团队,彼此协作边界不清晰,经常发生应用层开发有复用需求,提交至公共层下沉,公共层团队却因人力资源、全局通用性、二次加工等问题未承接,导致数据应用团队在 ADS 内部自建复用性模型以满足业务诉求。 - ADS 集市缺少统一规划,混乱低效:OneData 体系定义了公共层的数据域划分的标准和方法,但缺乏对 ADS 数据集市域的划分标准和方法。实操中集市大小体量差异悬殊,部分集市过度臃肿影响效率,而部分集市很小,且重复开发浪费资源。
- ADS 集市指标口径亟需统一:OneData 给出了指标定义较清晰的规范定义方法,但指标实操中均需拆解到业务过程,原子指标、修饰词、统计周期的设计成本高且口径各不一致,并随着业务发展指标口径越繁复,将严重影响 ADS 集市的开发效率。
- 缺少高质量的设计标准指导,模型千人千样,事后问题频发:OneData 体系包含架构分层、规范定义、使用维度建模理论,但如何设计出高质量的数据模型,当前主要依赖专家经验。虽然 OneData 给出了模型设计过程六大原则,而六大原则如何落地并没有案例和建议支撑。如高内聚低耦合从模型粒度指标相似性、消费频率情况给了建议,但在公共层设计的过程有哪些考虑因素,应用层没有外部消费又该如何操作?不合理的模型设计是核心应用产出不符合要求、模型变更频繁、架构稳定性差等问题的根源。
- 规范缺少配套的产品化工具落地保障:虽有 OneData 规范标准,但并未与开发工具 dataworks 集成形成规范管控力,出现了规范裸奔的现象。
4.2 数仓架构升级方案
核心解法思路在于从业务对数据的诉求出发,明确各模型分层的定位与作用,构建各模型层次的协作机制,形成高效的业务数据体系建设规范,并基于规范体系与开发平台共建模型设计和模型管控的产品化能力。
数据架构升级,明确各层次定位:数据体系的价值在于解决业务问题,业务对数据的诉求核心在交付效率、产出时效、质量可靠、成本可控;同时服务业务的不只是应用层,而应由贴源层、公共层、应用层共同服务。需要对各层次明确定位,形成合力而避免内耗:
- 应用层专注支撑业务,提升服务业务的效率,确保口径一致性,核心内容的稳定性;
- 公共层通过抽象复用提升应用层的效率,同时共性逻辑下沉确保全局一致性;因为考虑复用性,服务多个集市下游,也需要关注模型设计的易用性和稳定性;
- 贴源层在数据合规的背景下,需要关注数据接入的合规性;同时贴源层不做太多的逻辑转化,通过工具来提升接入效率。
升级完善 OneData 规范:补全集市域的划分标准与方法,通过对集市进行划
分降低应用层的复杂性,提升研发效率;并基于维度建模理论,结合业务设
计最佳实践,总结应用层、公共层的设计原则,提升模型设计的质量;同时
建立公共层的共建机制,形成公共层和应用层的联动。产品能力升级,确保模型规范有效执行:基于 OneData 方法论,在 dataworks
上构建智能建模产品能力,将模型设计过程产品化;同时基于设计原则,抽
象沉淀核心检测规则,形成事前管控能力,降低模型问题。
4.3 数仓架构升级建议
数据架构升级建议主要包含数据分层建议、公共层数据域建议、应用层集市域建议。
4.3.1 数据分层建议
升级分层架构建议(ODS/DWD/DIM/DWS/MDS/ADS),新增 MDS 层以解决应用层和公共层定位不清晰的问题;同时结合业务诉求的变化,完善各层次的内容范围。
ODS(操作数据层):把操作系统数据几乎无处理的存放在数据仓库系统中,采用增量或全量同步,根据业务需要保存历史数据、清洗数据。
- 关注数据安全合规:随着国家数安法、个保法的出台,跨域数据的获取需要遵循安全合规要求。
- 关注业务系统的稳定性:ODS 大部分来源于业务系统,需要采用正确的同步方式,同时避免重复同步,以保障业务系统的稳定性。
- 关注数据存储成本:ODS 层跟 DWD 层的粒度几乎一致,需要关注重复存储带来的成本压力。
公共层:公共层通过抽象集市复用逻辑,按业务过程重新定义数据,提升应用层的开发效率。因其具有通用性,需关注数据的易用性、稳定性,且易用性为主,稳定性为辅。
- DWD(明细层): 使用维度建模方法,采用维度退化手法将维度退化至事实表,以降低应用层频繁关联的成本,提升模型易用性;通过对业务过程进行重新建模,保障数据粒度与源系统保持一致,提升模型规范性便于上层数据的加工和计算;
- DIM(统一维度层):用于存放维度建模方法中核心构建总线矩阵的维度,同时 OLAP 场景常用分析的统计分析维表;确保跨集市统计分析维度的统一;
- DWS(汇总层):对 ADS 层的数据诉求进行通用化抽象,按业务过程和维度组织数据,沉淀共用指标,减少重复计算应用层研发效率,确保跨集市指标口径统一。
集市层:数据集市是满足特定的部门或者用户需求,按照业务角度组织数据,快速满足业务的数据层。数据集市需关注研发效率、口径一致性,以及核心应用的稳定性。
- ADS:面向具体应用场景构建,场景间松耦合,一般为专表专用,快速满足业务需求;
- MDS:用于承接单集市内多场景的数据共性问题,沉淀集市中间层,确保集市内维度、口径一致性与研发效率;因其承载集市内复用性能力,需要适当进行垂直切分解耦,以确保核心数据的产出稳定。
4.3.2 公共层数据域建议
对公共层进行数据域划分,使公共基础数据组织分类清晰呈现,便于提升数据消费者理解度,并优化数据管理。
具体数据域划分规范可参照:《模型命名规范-数据域划分规范》
请重点注意:
a. 数据域的抽象原则,根据高内聚低耦合的基础原则,以数据消费的内聚性、数据来源的内聚性、数据加工的内聚性进行抽象定义数据域标准;
b. 数据域的划分需统一标准,尽量符合 MECE 原则。
*MECE,是 Mutually Exclusive Collectively Exhaustive,中文意思是“相互独立,完全穷尽”。也就是对于一个重大的议题,能够做到不重叠、不遗漏的分类,而且能够借此有效把握问题的核心,并解决问题的方法。
4.3.3 应用层集市域建议
对应用层进行集市主题域划分,通过对相似数据业务场景内聚抽象进行分类,以降低 ADS 层的重复建设和数据管理复杂度,使应用研发更聚焦更高效。
请重点注意:
a.集市的划分原则,以业务场景或者服务对象作为划分原则,对相似数据业务场景内聚抽象进行分类;
b.集市域的划分需统一标准,尽量符合 MECE 原则。
三、数据仓库设计基础
重点掌握
- 请介绍你知道几种数据模型以及其特点。
- 请介绍一下第一、二、三范式。
- 列举一下你使用过的逆范式化设计,并说明这样做的目的?(逆 1nf-hive 里面的 map 结构;逆 2nf-log 日志表,做 PV、UV;逆 3nf-宽表设计)
- 请说明一下维度建模的过程。
- 请举例说明一下星型模型、雪花模型、星座模型,并说明它们各自的优缺点。
1. 关系数据模型
关系数据模型也叫做 E-R 模型,诞生于 20 世纪 70 年代,设计支持是针对关系型数据库的建模,比较适合构建三范式的模型,在数仓领域,Inmon 比较推崇此建模方式。
1.1 关系数据模型结构
从模型结构上看:
我们使用一个分公司-员工关系的例子。假设一个大型公司在全国都有分公司,每个员工属于一个分公司,一个分公司有一个经理,分公司经理也是公司员工。分公司-员工关系如图所示:
从构建流程上看:
1.1.1 关系
由行和列构成的二维结构,对应关系数据库中的表。如示例中的分公司表和员工表。。注意,这种认识只是我们从逻辑上看待关系模型的方式,并不应用于表在磁盘上的物理结构。
1.1.2 属性
由属性名称和类型名称构成的顺序对,对应关系数据库中表的列,如地址(Variable Characters)是公司表的一个属性。属性值是属性的一个特定的有效值,可以是简单的标量值,也可以是复合数据类型值。
在关系数据模型中,我们把关系描述为表,表中的行对应不同的记录,表中的列对应不同的属性。属性可以以任何顺序出现,而关系保持不变,也就是说,在关系理论中,表中的列是没有顺序的。
1.1.3 属性域
属性的取值范围。每一个属性都有一个预定义的值的范围。属性域是关系模型的一个重要特征,关系中的每个属性都与一个域相关。各个属性的域可能不同,也可能相同。域描述了属性所有可能的值。
1.1.4 元组
关系中的一条记录,对应关系数据库中的一个表行。元组可以以任何顺序出现,而关系保持不变,也就是说,在关系理论中,表中的行是没有顺序的。
1.1.5 关系数据库
一系列规范化的表的集合。这里的规范化可以理解为表结构的正确性
1.1.6 关系表的属性
关系表有如下属性:
- 每个表都有唯一的名称;
- 一个表中每个列有不同的名字;
- 一个列的值来自于相同的属性域;
- 列是无序的;行是无序的。
1.1.7 关系数据模型中的键
键是用于标识表(关系)中每一行(元组)的属性和属性集合.
超键
超键是指一个属性集,其值能够唯一标识表中的每一个元组,我们通常只对仅包含能够唯一标识记录的最小数量的列感兴趣。例如:在学生表中,学号和姓名可能共同构成一个超键
候选键
候选键是从超键中进一步提炼出来的,它是最小的属性集,仍然可以确保唯一性,每个候选键都可以作为主键使用,但通常在一个关系中只选择一个作为实际的主键。表的候选键有三个属性:- 唯一性:在每条记录中,候选键的值唯一标识该记录。
- 最小性:具有唯一性属性的超键的最小子集。
- 非空性:候选键的值不允许为空。
例如:在学生表中,如果学号本身能唯一确定一个学生,则学号就是一个候选键。
主键
主键是在候选键中被指定为表的主要标识符的一个或多个属性。它必须具有唯一性和非空性。即主键的值在表中不能重复,并且每个元组都必须有主键值。例子:在学生表中,如果选定学号为主键,则学号字段上会施加唯一约束.
主键的选择在关系数据模型中非常重要,很多性能问题都是由于主键选择不当引起的。
外键
外键是一个关系(表)中的某个属性,它的值引用来另一个关系(表)的主键。这种结构建立了两个表之间的关联或链接.例子:在课程成绩表中,可能有一个“学号”字段,它是“学生表”的主键的副本,在这里学号就是外键。主键所在的表被称为父表,外键所在的表被称为子表。
例子说明:
学生成绩信息表中有(学号、姓名、性别、年龄、系别、专业等),我们假设学生的姓名唯一,没有重名的现象。
超键:
学生表中含有学号或者身份证号的任意组合都为此表的超键。如:
(学号)、(学号,姓名)、(学号,性别),这些都唯一,是超键。
(姓名,性别)唯一,是一个超键。
(姓名,年龄)唯一,是一个超键。
(姓名,性别,年龄)唯一,是一个超键。
候选键:
学号唯一,没有多余属性,是一个候选键。
姓名唯一,没有多余属性,是一个候选键。
(姓名,性别)唯一,但是单独姓名一个属性就能确定这个人是谁,所以性别这个属性就是多余属性,(姓名,性别)不是候选键,同样(姓名,年龄),(姓名,性别,年龄),也不是候选键。
主键:主键就是候选键里面的一个,是人为规定的,例如学生表中,我们通常会让“学号”做主键,学号能唯一标识这一个元组。
外键:外键就很简单了,假如我们还有一个老师表,每个老师都有自己的编号,假设老师编号在老师这个层次中是主键,在学生表中它就是外键。
1.2 关系数据模型中的完整性
1.2.1 空值
表示一个列的值目前还不知道或者对于当前记录来说不可用。空值可以意味着未知,也可以意味着某个记录没有值,或者只是意味着该值还没有提供。空值是处理不完整数据或异常数据的一种方式。空值与数字零或者空字符串不同,零和空字符串是值,但空值代表没有值。因此,空值应该与其他值区别对待。空值具有特殊性,当它参与逻辑运算时,结果取决于真值表。每种数据库系统对空值参与运算的规则定义也不尽相同。
1.2.2 关系完整性规则
关系数据模型中的完整性是指数据库中数据的正确性、一致性和有效性,以确保存储的数据满足特定业务规则和语义约束。关系数据库通过实施完整性约束来维护数据完整性,这些约束可以分为三大类:
实体完整性(Entity Integrity)
- 实体完整性要求关系(表)中的主键列(Primary Key)具有唯一且非空的值。
- 每一行都必须有一个唯一的标识符,这意味着在主键列上不允许出现重复值,并且主键列不能包含 NULL 值。
参照完整性(Referential Integrity)
参照完整性是针对两个相关表之间引用关系的规定。
当一个表中的外键(Foreign Key)引用了另一个表的主键时,参照完整性保证:外键的值要么与被引用表的主键值相匹配(存在性),要么为 NULL(如果允许的话);如果删除或更新被引用表中主键对应的记录,则需要根据相应的级联规则(如:CASCADE, SET NULL, RESTRICT 等)来决定是否允许操作以及如何影响关联的外键记录。
例如:在学生选课表和课程表中,学生选课表中的课程 CourseID 必须是课程表中存在的 CourseID 或者允许为 NULL。
用户定义的完整性(User-defined Integrity)
用户定义完整性是指由数据库应用程序开发人员根据具体应用领域的需求制定的一组额外的完整性约束条件。
这些约束可以是对任何属性或属性集合的限制,例如规定某个数值范围、检查字符串长度、日期的有效性或者禁止某些组合等。
1.2.3 业务规则
定义或约束组织的某些方面的规则。业务规则的例子包括属性域和关系完整性规则。属性域用于约束特定列能够取的值。有些数据库系统,如 Oracle,支持叫做 check 的约束,也用于定义列中可以接受的值,但这种约束是定义在属性域之上的,比属性域的约束性更强。例如,员工表的性别列就可以加上 check 约束,使它只能取有限的个值。
1.2.4 关系数据库语言
关系数据库的主要语言是 SQL 语言,SQL 是 Structured Query Language 的缩写,意为结构化查询语言。
(1)DDL 是 Data Definition Language 的缩写,意为数据定义语言,用于定义数据库结构和模式。典型的 DDL 有 create、alter、drop、truncate、comment、rename等。
(2)数据查询语言 (DQL - Data Query Language),DQL 主要用于从数据库中检索信息而不进行任何修改。SELECT 语句是最主要的 DQL 操作,用于查询特定的数据,并可以结合 WHERE、GROUP BY、HAVING 和 ORDER BY 等子句来筛选、分组、过滤和排序数据。
(3)DML 是 Data Manipulation Language 的缩写,意为数据操纵语言,用于检索、管理和维护数据库对象。典型的 DML 有 select、insert、update、delete、merge、call、explain、lock 等。
(4)DCL 是 Data Control Language 的缩写,意为数据控制语言,用于授予和回收数据库对象上的权限。典型的 DCL 有 grant 和 revoke。
(5)TCL 是 Transaction Control Language 的缩写,意为事务控制语言,用于管理DML 对数据的改变。它允许一组 DML 语句联合成一个逻辑事务。典型的 TCL 有commit、rollback、savepoint 等。
1.3 关系数据模型中的规范化
(☆☆☆重点记忆)
关系数据模型的规范化是一种数据组织的技术。规范化方法对表进行分解,以消除数据冗余,避免异常更新,提高数据完整性。
如果没有规范化,数据的更新处理将变得困难,异常的插入、修改、删除数据的操作会频繁发生。为了便于理解,来看下面的例子。
假设有一个 employee 的员工表,它有九个属性:id(员工编号)、name(员工姓名),mobile(电话)、zip(邮编)、province(省份)、city(城市)、district(区县)、deptNo(所属部门编号)、deptName(所属部门名称),表中的数据如表所示。
由于此员工表是非规范化的,我们将面对如下的问题。
- 修改异常:上表中张三有两条记录,因为他隶属两个部门。如果我们要修改张三的地址,必须修改两行记录。假如一个部门得到了张三的新地址并进行了更新,而另一个部门没有,那么此时张三在表中会存在两个不同的地址,导致了数据不一致。
- 新增异常:假如一个新员工加入公司,他正处于入职培训阶段,还没有被正式分配到某个部门,如果 deptNo 字段不允许为空,我们就无法向该表中新增该员工的数据。
- 删除异常:假设公司撤销了 D3 这个部门,那么在删除 deptNo 为 D3 的行时,会将李四的信息也一并删除。因为他只隶属于 D3 这一个部门。
为了克服这些异常更新,我们需要对表进行规范化设计。规范化是通过应用范式规则实现的。
什么是范式?简言之就是,数据库设计对数据的存储性能,还有开发人员对数据的操作都有莫大的关系。所以建立科学的、规范的数据库是需要满足一些规范的来优化数据存储方式,在关系型数据库中这些规范就可以称为范式。
常用的范式有第一范式(1NF)、第二范式(2NF)、第三范式(3NF)。
1.3.1 第一范式
每个属性(字段)的值必须是原子的(不可再分)。
上例中张三有两个手机号存储在 mobile 列中,违反了 1NF 规则。为了使表满足 1NF,数据应该修改为如下表所示。
1.3.2 第二范式
第二范式要同时满足下面两个条件: 1.满足第一范式,2.非主属性必须完全依赖于整个候选键,不能存在部分依赖。
例如,员工表的一个候选键是{id,moblie,deptNo},而 deptName 依赖于(deptNo),同样 name 仅依赖于{id},因此不是 2NF 的。为了满足第二范式的条件,需要将这个表拆分成 employee、dept、employee_dept、employee_mobile 四个表,如表所示。
1.3.3 第三范式
第三范式要同时满足下面两个条件:== 1.满足第二范式;2.没有传递依赖==。
例如,员工表的 province、city、district 依赖于 zip,而 zip 依赖于(id),换句话说,province、city、district 传递依赖于(id),违反了 3NF 规则。为了满足第三范式的条件,可以将这个表拆分成 enployee 和 zip 两个表,如下所示。
这里再解释一下传递依赖:
数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。像:a–>b–>c 属性之间含有这样的关系,是不符合第三范式的。
举个例子,比如 Student 表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话),这样一个表结构,就存在上述关系:
学号–> 所在院校 --> (院校地址,院校电话)
这样的表结构,我们应该拆开来,如下:
(学号,姓名,年龄,性别,所在院校)–(所在院校,院校地址,院校电话)
2. 维度数据模型
维度模型诞生于 20 世纪 90 年代,由 Kimball 引入到数仓领域并形成数仓维度建模的理论体系,比较适合分析领域。
事实和维度是两个维度模型中的核心概念。事实表示对业务数据的度量,而维度是观察数据的角度。事实通常是数字类型的,可以进行聚合和计算,而维度通常是一组层次关系或描述信息,用来定义事实
。
例如,销售金额是一个事实,而销售时间、销售的产品、购买的顾客、商店等都是销售事实的维度。
事实表记录了特定事件的数字化的考量,一般由数字值和指向维度表的外键组成。通常会把事实表的粒度级别设计得比较低,使得事实表可以记录很原始的操作型事件,但这样做的负面影响是累加大量记录可能会更耗时。事实表有以下三种类型:
事务事实表
。统计某个时间点事件发生情况。每发生一次事件,表中就会插入一条记录,且发生的事件不会被修改,只能新增,如下单记录、支付记录、券领取记录。周期快照事实表
。统计某个时间区间内业务过程的当前状况或历史状况平均,如月底账户余额、每天、周、月商品销量。累积快照事实表
。统计流程节点运转效率。一般此类表有多个关键日期字段,且事务事件跨度不会太长,比如订单状态:下单、支付、发货、确认收货。
维度表的记录数通常比事实表少,但每条记录包含有大量用于描述事实数据的属性字段。维度表可以定义各种各样的特性,以下是几种最常用的维度表。
- 时间维度表。描述星型模式中记录的事件所发生的时间,具有所需的最低级别的时间粒度。数据仓库是随时间变化的数据集合,需要记录数据的历史,因此每个数据仓库都需要一个时间维度表。
- 地理维度表。描述位置信息的数据,如国家、省份、城市、区县、邮编等。
- 产品维度表。描述产品及其属性。
- 人员维度表。描述人员相关的信息,如销售人员、市场人员、开发人员等。。
- 范围维度表。描述分段数据的信息,如高级、中级、低级等。
2.1 维度数据模型建模过程
(☆☆☆重点记忆)
从模型结构上看:
从构建流程上看:
维度模型设计期间主要涉及 4 个主要的过程:
(1)选择业务过程
(2)声明粒度
(3)确认维度
(4)确认事实
在建模期间,需要考虑业务需求以及协作建模阶段涉及的底层数据源。从上述维度建模步骤来看,重点在解决数据粒度、维度设计和事实表设计问题。
声明粒度,为业务最小活动单元或不同维度组合。以共同粒度从多个组织业务过程合并度量的事实表称为合并事实表,需要注意的是,来自多个业务过程的事实合并到合并事实表时,它们必须具有同样等级的粒度。
由于在维度建模过程中,涉及到很多概念。下面通过一个场景来,来一一说明。
例如:常见的电商下单环节,每个用户提交一笔订单(仅限一个物品),就对应于一条订单记录。
【业务过程】:下订单
【粒度】:每笔订单(拆分为单个物品)
【维度】:用户、商品、卖家、地域、渠道类型等(可供分析的角度)
【事实/度量】:订单金额等(可用于分析的数据)
2.1.1 需求收集
收集业务需求,在开始维度建模工作之前,需要理解业务需求,以及作为底层源数据的实际情况。通过与业务方沟通交流、查看现有报表等来发现需求,用于理解他们的基于关键性能指标、决策制定过程、支持分析需求的目标。同时,数据实际情况可通过与后端系统开发同事交流,了解访问数据可行性等。
2.1.2 业务过程
选择业务过程,业务过程是组织完成的操作型活动。理清业务过程,就是需要建立或获取性能度量,并转换为事实表中的事实。多数事实表关注某一业务过程的结果。过程的选择非常重要的,因为过程定义了特定的设计目标以及对粒度、维度、事实的定义。
2.1.3 声明粒度
声明粒度,声明粒度是维度设计的重要步骤。粒度用于确定某一事实表中的行表示什么。在选择维度或事实前必须声明粒度,因为每个候选维度或事实必须与定义的粒度保持一致。在从给定的业务过程获取数据时,原子粒度是最低级别的粒度。强烈建议从关注原子级别粒度数据开始设计,因为原子粒度数据能够承受无法预期的用户查询。
2.1.4 确认维度
确认维度(描述环境),维度提供围绕某一业务过程事件所涉及的“谁、什么、何处、何时、为什么、如何”等背景。维度表包含分析应用所需要的用于过滤及分类事实的描述性属性。牢牢掌握事实表的粒度,就能够将所有可能存在的维度区分开来。
2.1.5 确认事实
确认事实(用于度量),涉及来自业务过程事件的度量,基本上都是以数据值表示。一个事实表行与按照事实表粒度描述的度量事件之间存在一对一关系,因此事实表对应一个物理可观察的事件。在事实表内,所有事实只允许与声明的粒度保持一致。
例如:权益核销环节,每个用户下一笔订单(为方便退单,商品粒度为 sku),就对应于多条权益核销记录。
【业务过程】:券核销业务过程
【粒度】:子订单 + 券 id(拆分为单个 sku)
【维度】:权益、领取场景、发放渠道等(分析视角)
【事实/度量】:核销金额,引导 GMV 等(原子指标)
2.2 维度规范化
与关系模型类似,维度也可以进行规范化。对维度的规范化(又叫雪花化),目的是减少数据冗余,提高数据一致性以及优化查询性能。减少冗余属性,是对非规范化维度做的规范化处理,在下面介绍雪花模型时,会看到维度规范化的例子。一个非规范化维度对应一个维度表,规范化后,一个维度会对应多个维度表,维度被严格地以子维度的形式连接在一起。实际上,在很多情况下,维度规范化后的结构等同于一个低范式级别的关系型结构。
设计维度数据模型时,会因为如下原因而不对维度做规范化处理:
- 规范化会增加表的数量,使结构更复杂。
- 不可避免的多表连接,使查询更复杂。
- 查询性能原因。分析查询需要聚合计算或检索很多维度值,此时第三范式的数据会遭遇性能问题。如果需要的仅仅是操作型报表,可以使用第三范式,因为作型系统的用户需要看到更细节的数据。
正如在前面关系模型中提到的,对于是否应该规范化的问题存在一些争论。
总体来说,当多个维度共用其些通用的属性时,做规范化会是有益的。例如,客户和供应商都有省、市、区县、街道等地理位置的属性,此时分离出一个地区属性就比较合适。
2.3 建模特点
2.3.1 易理解
相对于规范化的关系模型,维度模型容易理解且更直观。在维度模型中,信息按业务种类或维度进行分组,这会提高信息的可读性,也方便了对于数据含义的解释。简化的模型也让系统以更为高效的方式访问数据库。关系模型中,数据被分布到多个离散的实体中,对于一个简单的业务流程,可能需要很多表联合在一起才能表示。
2.3.2 高性能
维度模型更倾向于非规范化,因为这样可以优化查询的性能。介绍关系模型时多次提到,规范化的实质是减少数据冗余,以优化事务处理或数据更新的性能。
这里用一个具体的例子进一步说明性能问题。
如图所示,左边是一个销售订单的典型的规范化表示。
订单(Order)实体描述有关订单整体的信息,订单明细(Order Line)实体描述有关订单项的信息,两个实体都包含描述其订单状态的信息。
右边是一个订单状态维(Order Status Dimension),该维表描述订单和订单明细中对应的状态编码值的唯一组合。它包括在规范化设计的订单和订单明细实体中都出现的属性。当销售订单事实行被装载时,参照在订单状态维中的适合的状态编码的组合设置它的外键。
维度设计的整体观点是要简化和加速查询。假设有 100 万订单,每个订单有10 条明细,订单状态和订单明细状态各有 10 种。
如果用户要查询某种状态特性的订单,按 3NF 模型,逻辑上需要关联 100 万记录与 1000 万记录的两个大表,然后过滤两个表的状态值得到所要的结果。另一方面,销售订单事实表(图中并没有画出)按最细数据粒度有 1000 万记录,3NF里的订单表属性在事实表里是冗余数据,状态维度有 100 条数据,只需要关联1000 万记录与 100 条记录的两个表,再进行状态过滤即可。
2.3.3 可扩展
维度模型是可扩展的。由于维度模型允许数据冗余,因此当向一个维度表或事实表中添加字段时,不会像关系模型那样产生巨大的影响,带来的结果就是更容易容纳不可预料的新增数据。这种新增可以是单纯地向表中增加新的数据行而不改变表结构,也可以是在现有表上增加新的属性。基于数据仓库的查询和应用不需要过多改变就能适应表结构的变化。历史的查询和应用会维续工作而不会产生错误的结果。但是对于规范化的关系模模型,由于表之间存在复杂的依赖关系,改变表结构前一定要仔细考虑。
2.4 星型模型
(☆☆☆重点记忆)
2.4.1 什么是星型模型
星型模式是维度模型最简单的形式,也是数据仓库以及数据集市开发中使用最广的形式。星型模式由事实表和维度表组成,一个星型模式中可以有一个或多个事实表,每个事实表引用任意数量的维表。
Sales Fact 是唯一的事实表,Dim_time、Dim_branch、Dim_location 和 Dim_item是四个维度表。每个维度表的 xxx_key 字段是它们的主键。
星型模式的物理模型像一颗星星的形状,中心是一个事实表,围绕在事实表周围的维度表表示星星的放射状分支,这就是星型模式这个名字的由来。
举个例子:星型模式将业务流程分为事实和维度。事实包含业务的度量,是定量的数据,如销售价格、销售数量、距离、速度、重量等。维度是对事实数据属性的描述。如日期、产品、客户、地理位置等是维度。
星型模型的特点:
- 星型模式中的每个维都由唯一的一维表表示;
- 维度表应包含属性集;
- 使用外键将维度表连接到事实表;
- 维度表未相互连接;
- 事实表将包含键和度量;
- 维度表并未规范化 normalized;
2.4.2 优点
星型模式是非规范化的,在星型模式的设计开发过程中,不受应用于事务型关系数据库的范式规则的约束。星型模式的优点如下:
- 简化查询。查询数据时,星型模式的连接逻辑比较简单,而从高度规范化的事实表查询数据时,往往需要更多的表连接。
- 简化业务报表逻辑。与高度规范化的模式相比,由于查询更简单,因此星型模式简化了普通的业务报表(如每月报表)逻辑。
- 获得查询性能。星型模式可以提升只读报表类应用的性能。
- 快速聚合。基于星型模式的简单查询能够提高聚合操作的性能。
- 各类 BI 工具广泛支持该架构,便于向立方体提供数据。星型模式被广泛用于高效地建立OLAP 立方体,几乎所有的 OLAP 系统都提供 ROLAP 模型(关系型 OLAP),它可以直接将星型模式中的数据当作数据源,而不用单独建立立方体结构。
2.4.3 缺点
星型模式的主要缺点是不能保证数据完整性。一次性地插入或更新操作可能会造成数据异常,而这种情况在规范化模型中是可以避免的。
- 插入异常
假设销售主题域的数据仓库,其中事实表“SalesFact”和维度表“Customer”,“Product”,“Time”。在星型模型中,“SalesFact”直接连接到各个维度表。如果在一次批量加载过程中,新客户记录没有同时添加到“Customer”维度表,而此时有该客户的销售记录已经插入到了“SalesFact”表中,则会造成参照完整性问题。因为“SalesFact”中的外键(如 CustomerID)将指向一个不存在于“Customer”表中的记录。 - 更新异常
在同样的场景下,假设某个产品信息发生了变化(例如产品名称或分类),由于星型模型通常不支持对历史数据的更新,若直接修改“Product”维度表,可能会导致之前存储的与该产品相关的所有销售记录(即“SalesFact”表中的记录)在分析时关联到新的产品信息,从而破坏了历史数据分析的一致性与准确性。 - 事务一致性
星型模型主要用于读取密集型场景,而非事务处理场景。因此,在实时插入或更新操作中,难以保证 ACID(原子性、一致性、隔离性和持久性)事务原则。
例如,当多个业务操作同时发生时,如果没有适当的并发控制机制,可能会造成数据的临时不一致状态。
星型模式的数据装载,一般都是以高度受控的方式,用批处理或准实时过程执行的,以此来抵消上面提到的不足。
星型模式的另一个缺点是对于分析需求来说不够灵活。它更偏重于为特定目的建造数据视图,因此实际上很难进行全面的数据分析。星型模式不能自然地支持业务实体的多对多关系,需要在维度表和事实表之间建立额外的桥接表。
2.5 雪花模型
(☆☆☆重点记忆)
2.5.1 什么是雪花模型
雪花模式是星型模式的扩展,在雪花模式中,每个维度都经过规范化并连接到更多维度表,其实体关系图有类似于雪花的形状,因此得名。
与星型模式相同,雪花模式也是由事实表和维度表所组成。所谓的“雪花化”就是将星型模式中的维度表进行规范化处理。当所有的维度表完成规范化后,就形成了以事实表为中心的雪花型结构,即雪花模式。
将维度表进行规范化的具体做法是,把低基数的属性从维度表中移除并形成单独的表。基数指的是一个字段中不同值的个数,如主键列具有唯一值,所以有最高的基数,而像性别这样的列基数就很低。
2.5.2 数据规范化与存储
规范化的过程就是将维度表中重复的组分离成一个新表,以减少数据冗余的过程。正因为如此,规范化不可避免地增加了表的数量。在执行查询的时候,不得不连接更多的表。但是规范化减少了存储数据的空间需求,而且提高了数据更新的效率。
从存储空间的角度看,典型的情况是维度表比事实表小很多。这就使得雪花化的维度表相对于星型模式来说,在存储空间上的优势没那么明显了。
举例来说,假设某个大型连锁商超在 220 个区县的 300 个商场,共有 100 万条销售记录。
星型模式的设计会产生 1000300 条记录,其中事实表 1000000 条记录,商场维度表有 300 条记录,每个区县信息作为商场的一个属性,显式地出现在商场维度表中。
在规范化的雪花模式中,会建立一个区县维度表,该表有 220 条记录,商场表引用区县表的主键,有 300 条记录,事实表没有变,还是 1000000 条记录,总的记录数 1000520(1000000+300+220)。
在这种情况(作为子表的商场记录数和父表的区县记录数差别不大)下,星型模式所需的空间反而比雪花模式要少。如果商场有 10000 家,情况就不一样了,星型模式的记录数是 1010000,雪花模式的记录数是 1010220,从记录数上看,还是雪花模型多。但是,星型模式的商场表中会有 10000 个冗余的区县属性信息,而在雪花模式中,商场表中只有 10000个区县的主键,而需要存储的区县属性信息只有 220 个,当区县的属性很多时,会大大减少数据存储占用的空间。
有些数据库开发者采取一种折中的方式,底层使用雪花模型,上层用表连接建立视图模拟星型模式。这种方法既通过对维度的规范化节省了存储空间,同时又对用户屏蔽了查询的复杂性。但是当外部的查询条件不需要连接整个维度表时,这种方法会带来性能损失。
2.5.3 优点
雪花模式是和星型模式类似的逻辑模型。实际上,星型模式是雪花模式的一个特例(维度没有多个层级,只有一层)。某些条件下,雪花模式更具优势:
- 在 Schema 中新增一个维度将变得更加容易;
- 规范化的维度属性节省存储空间。
例子:在电商数据仓库,星型模型中有一个商品销售事实表(Fact_Sales)和一个扁平化的维度表(Dim_Product)。结构如下:
在雪花模型中,商品维度可能是这样设计的:
现在业务需求变化,需要引入一个新的维度 — — “商品子类别”(SubCategory)。
在星型模型中,只能改变现有模型的情况下添加这个新维度,需要在Dim_Product 维度表中新增一个列SubCategoryName(蓝色字段)。
在雪花模型中,只需在 Dim_SubCategory 表中增加相应的记录,由于Dim_Product 已经通过 CategoryID 与 Dim_Category 关联,而 Dim_Category 又可以轻松地通过 SubCategoryID 与新的 Dim_SubCategory 关联,所以无需对事实表或者现有的维度表做任何修改。这种设计允许我们在不影响现有模型的基础上,更加灵活地添加、更新或扩展维度信息。
2.5.4 缺点
雪花模型的主要缺点是维度属性规范化增加了查询的连接操作和复杂度。相对于平面化的单表维度,多表连接的查询性能会有所下降。但雪花模型的查询性能问题近年来随着数据浏览工具的不断优化而得到缓解。
和具有更高规范化级别的事务型模式相比,雪花模式并不确保数据完整性。
向雪花模式的表中装载数据时,一定要有严格的控制和管理,避免数据的异常插入或更新。
2.6 星座模型
数据仓库是由多个主题组成的,包含多个事实表,而维表是公共的,可以被多个事实表关联使用,这种模式可以看做星型模式的汇集,因而称作星系模式或者事实星座模式。
星座模式是数据仓库最常使用的数据模式,尤其是企业级数据仓库(EDW)。
这也是数据仓库区别于数据集市的一个典型的特征,从根本上而言,数据仓库数据模型的模式更多是为了避免冗余和数据复用,套用现成的模式,是设计数据仓库最合理的选择。
“事实星座模式”的架构。这种设计强调避免冗余和数据复用,从而支持整个企业的决策分析需求。相比之下,数据集市则通常是针对某个特定业务领域或部门的需求建立的小规模、快速响应的数据集合,它们可能没有统一的维度模型和共享维度表。
2.7 星型模型 VS 雪花模型
星型模型因为数据的冗余所以很多统计查询不需要做外部的连接,因此一般情况下效率比雪花型模型要高。
星型结构不用考虑很多正规化的因素,设计与实现都比较简单。雪花型模型由于去除了冗余,有些统计就需要通过表的联接才能产生,所以效率不一定有星型模型高。正规化也是一种比较复杂的过程,相应的数据库结构设计、数据的ETL、以及后期的维护都要复杂一些。因此在冗余可以接受的前提下,实际运用中星型模型使用更多,也更有效率。
通过上面的对比我们可以发现,在数据仓库建设中大多时候比较适合使用星型模型构建底层数据 Hive 表,通过大量的冗余来提升查询效率,星型模型对 OLAP的分析引擎支持比较友好,这一点在 Kylin 中比较能体现。而雪花模型在关系型数据库中如 MySQL,Oracle 中非常常见,尤其像电商的数据库表。在数据仓库中雪花模型的应用场景比较少,但也不是没有,所以在具体设计的时候,可以考虑是不是能结合两者的优点参与设计,以此达到设计的最优化目的。
3. Data Vault 模型(了解即可)
3.1 Data Vault 模型简介
Data Vault 是面向细节的、可追踪历史的、一组有连接关系的规范化表的集合。这些表可以支持一个或多个业务功能,它是一种综合了第三范式(3NF)和星型模型优点的建模方法。其设计理念是要满足企业对灵活性、可扩展性、一致性和对需求的适应性要求,是一种专为企业级数据仓库量身定制的建模方式。
Data Vault 方法需要跟踪所有数据的来源,因此其中每个数据行都要包含数据来源和装载时间属性,用以审计和跟踪数据值所对应的源系统。
3.2 Data Vault 模型组成部分
从模型结构上看:
Data Vault 模型是一种中心辐射式模型,其设计重点围绕着业务键的集成模式。这些业务键是存储在多个系统中的、针对各种信息的键,用于定位和唯一标识记录或数据。Data Vault 模型主要分为三种类型的表:中心表(Hub)、链接表(Link)、卫星表/附属表(Satellite),其中中心表记录业务主键,链接表记录业务关系,卫星表记录业务描述。
下面来看一个实际的例子:如下图,hub—中心表、link—链接表、sat—卫星表。
从构建流程上看:
3.2.1 中心表(Hub 组件)
中心表用来保存一个组织内的每个实体的业务主键,业务主键唯一标识某个业务实体。中心表和源系统表是相互独立的。当一个业务主键被用在多个系统时,它在 Data Vault 中也只保留一份,其他的组件都能接到这一个业务主键上。这就意味着业务数据都集成到了一起。
- hub_key 代理主键,通过对业务主键进行 MD5 计算所得
- business_key业务主键,唯一标识业务主键,来自业务的源系统
- load_dts 数据第一次装载到数据仓库时系统生成的时间
- rec_src 定义了数据来源(例如数据源系统或表)
3.2.2 链接表(Link 组件)
链接表是不同中心表之间的关系链接,链接表一般由一组外键字段构成,通过存储相关业务实体间 Hub 表的代理主键(Surrorgate Key),以记录一对多、多对多的业务实体间关系,如员工与企业的雇佣关系,账户与客户之间的关系等。链
接表主要包括主键、外键 1、···、外键 n、装载时间戳、数据来源系统等字段构成,其中主键对应多个外键的唯一组合,一般是与业务无关的序列数值。
- link_key代理主键,使用相关的父 Hub 表的业务主键拼接后计算 MD5值
- hub_keys hubs 的代理键
- hub_business_keys hubs 的业务主键
- load_dts 第一次装载数据的时间
- rec_src 源系统信息
3.2.3 附属表(Satellite 组件)
附属表用于保存中心表和链接表的描述属性,包含了所有历史变化数据,附属表有且仅有一个唯一外键关联到中心表或链接表。
Hub 表中业务主键所对应的业务描述,即业务实体的属性信息,这些信息具有时效性,随时间变化而可能产生变化,因此,Satellite 组件内的记录均具有时间维,可记录实体属性的历史变化情况。根据实体属性变化频率的不同,可将一类实体的业务属性分为若干 Satellite 表,通过向 Satellite 分表追加记录,以实现在更小粒度下,实现第二类渐变维的保存历史数据特性。
Satellite 组件包括以下关键字段:
- sta_key代理主键,相关的 hub 或 link 表的主键和数据载入时间的 MD5值
- hub_or_link_key 父 hub 或 Link 的代理主键
- attribute_columns 属性数据列
- hash_diff 各列拼接后的 MD5 值计算
- sat_load_dts 数据装载时间
- sat_rec_src 数据来源信息
3.3 Data Vault 模型的特点
一个设计良好的 Data Vault 模型应该具有以下特点:
- 所有数据都基于时间来存储,即使数据是低质量的,也不能在 ETL 过程中处理掉。依赖越少越好。
- 和源系统越独立越好。
- 设计上适合变化:源系统中数据的变化;在不改变模型的情况下可扩展。
- ETL 作业可以重复执行。
- 数据完全可追踪。
4. 各模型对比
四、Kimball 维度建模
重点掌握:
- 请说明一下你了解哪几种事实表?说说它们各自的特点和应用场景。
- 维度设计过程中有什么经验分享一下?
- 如何处理缓慢变化维度?
1.维度建模简介
在数据仓库领域,经常会听到两个名字,一个是 Bill Inmon 和 Ralph Kimball。Inmon 最早提出数据仓库的概念,在构建数据仓库过程中,主张自顶向下的设计,先设计好数仓的整体架构,然后进行局部设计,而 Kimball 正好相反,主张自底
向上设计,先根据各个业务主题进行设计,然后通过维度模型将数据仓库整合起来。目前 Kimball 的维度建模普遍被大家采用。
Inmon 构建数仓过程,从全局角度出发,主张采用三范式进行构建,该模式数据冗余小,但是在使用时相对繁琐,通常需要多次关联才能取到我们需要的数据。
Kimball 模型通常采用维度建模,如下图所示。维度模型通常由事实表和维度表构成。
维度建模通常有两种方式,一种是星型模型,另外一种是雪花模型;星型模型,是一种非常简单常用的模型,一个或者多个事实表和多个和事实表关联的维度表,就构成了星型模型,其实星型模型是雪花模型的一种特例。如下图的例子,一张事实表和 3 张维度表构成了一个星型模型。
雪花模型,同样由事实表和维度表构成,但是雪花模型中的维度表是需要经过标准化的,维度表经过类似星型模型的建模而成,如下面的一个例子;
2. 事实表设计
事实表作为数据仓库维度建模的核心,紧紧围绕着业务过程来设计,通过获取描述业务过程的度量来表达业务过程,包含了引用的维度和与业务过程有关的度量。
事实表的设计需要遵循一些原则和方法。在 Kimball 所著的《数据仓库工具箱》一书中,对于维度模型设计采用的 4 步设计方法:1.选择业务过程 2.声明粒度 3.确定维度 4.确定事实。
2.1 事实表基础
2.1.1 事实表特性
事实表中一条记录所表达的业务细节程度被称为粒度。粒度通常可以通过两种方式来表述:一种是维度属性组合所表示的细节程度,一种是所表示的具体业务含义。
作为度量业务过程的事实,一般为整型或浮点型的十进制数值,有可加性、半可加性和不可加性三种类型。
可加事实:可加事实指的是该度量可以按照和事实表关联的任一维度进行汇总。比如订单金额,可以按照品类维度进行汇总,按照店铺维度进行汇总等等。
半可加事实:指的就是该度量在某些维度下不可进行汇总,或者说汇总起来没有意义,比如说价差额,价差额在时间维度下的汇总就没有意义。
记录静态数据(仓库库存数据,金融账户余额)的所有度量针对于日期属性以及其它可能维度天然具有非可加性,但是例如库存数据针对产品种类或者商店汇总,是可加的,所以这种数据就是半可加事实。
不可加事实:指的是该度量在所有与该事实表关联的维度下都不可进行汇总,比如比率型数据,对于这种数据,如果确实是有汇总的必要,可以将其分子分母分别存储,然后在最后汇总之后再进行除法操作,从而得到“汇总”后的比率型数据。
相比维度表来说,通常事实表要细长的多,行的增加速度也比维度表快很多。
维度属性也可以存储到事实表中,这种存储到事实表中的维度列被称为退化维度。与其他存储在维度表中的维度一样,退化维度也可以用来作为事实表的过滤查询、实现聚合操作等。(常用的维度就可以退化到事实表中)
事实表有三种类型:事务事实表、周期快照事实表、累积快照事实表
- 事务事实表用来描述业务过程,跟踪空间或时间上某点的度量事件,保存的是最原子的数据,也称为“原子事实表”。
- 周期快照事实表以具有规律性的、可预见的时间间隔记录事实,时间间隔比如每天、每周、每月、每年等。
- 累积快照事实表用来表述过程开始和结束之间的关键步骤事件,覆盖了过程的整个生命周期,通常具有多个日期字段来记录关键时间点,当过程随着生命周期不断变化时,记录也会随着过程的变化而被修改。
2.1.2 事实表设计原则
- 原则 1:尽可能包含所有与业务过程相关的事实
- 原则 2:只选取与业务过程相关的事实
- 原则 3:分解不可加事实为可加的组件
- 原则 4:在选择维度和事实之前必须先声明粒度
- 原则 5:在同一个事实表中不能有多种不同粒度的事实
- 原则 6:事实的单位要保持一致
- 原则 7:对事实的 null 值要处理
- 原则 8:使用退化维度提高事实表的易用性
2.1.3 事实表设计方法
第一步:选择业务过程及确定事实表类型
业务过程通常用行为动词表示,表示业务执行的活动。比如图中的订单流转业务过程有四个:创建订单、买家付款、卖家发货、买家确认收货。在明确了流程所包含的业务过程后,需要根据具体的业务需求来选择与维度建模有关的业务过程。比如选择买家付款这个业务过程;还是选择创建订单和买家付款这两个业务过程,具体根据业务情况来确定。第二步:
声明粒度
粒度的声明是事实表建模非常重要的一步,意味着精确定义事实表的每一行表示什么业务含义,粒度传递的是与事实表度量有关的细节层次。明确的粒度能确保对事实表中每一行记录的理解不会产生混淆,保证所有的事实按照同样的细节层次记录。应该尽量选取最细级别的原子粒度,以确保事实表的应用具有最大的灵活性。同时对于订单过程而言,粒度可以被定义为最细的订单级别。比如订单中有父子订单的概念,即一条子订单对应一类商品,如果拍下了多种商品,则每种商品对应一条子订单;这些子订单一同结算的话,则会生成一条父订单。那么在这个例子中,事实表的粒度应该选择为子订单级别。
第三步:确定维度
完成粒度声明以后,也就意味着确定了主键,对应的维度组合以及相关的维度字段就可以确定了,应该选择能够描述清楚业务过程所处的环境的维度信息。比如订单付款事务事实表中,粒度为子订单,相关的维度信息有买家维度、卖家维度、商品维度、收货人信息、业务类型、订单时间等。
第四步:确定事实
事实可以通过回答“业务过程的度量是什么”来确定。应该选择与业务过程有关所有事实,且事实的粒度要与所声明的事实表粒度一致。事实有可加性、半可加性、非可加性事实,需要将不可加事实分解为可加的组件。在订单付款事务事实表中,同粒度的事实有子订单分摊的支付金额、邮费、优惠金额等。
第五步:冗余维度
在传统的维度建模星型模型中,对于维度的处理是需要单独存放在专门的维度表中,通过事实表中外键的方式获取维度。这样做的目的是为了减少事实表的维度冗余,从而减少存储消耗。而在大数据的事实表模型设计中,更多的考虑是提高下游用户的使用效率,降低数据获取的复杂性,减少关联的表数量。所以通常的事实表中会冗余方便下游用户使用的常用维度,以实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等操作。比如在电商订单付款事务事实表中,通常会冗余大量的常用维度字段,以及商品的类目、卖家店铺等维度信息。
2.2 事务事实表
2.2.1 事务事实表设计过程
事务,任何类型的事件都可以被理解为一种事务,比如交易过程中的创建订单、买家付款,物流过程中的揽货、发货、签收,退款中的申请退款、申请小二介入等等都可以理解为一种事务,事务事实表,即针对这些过程构建的一类事实表,用以跟踪定义业务过程的个体行为,提供丰富的分析型能力,作为数据仓库原子的明细数据。下面以电商交易事务事实表为例子阐述事务事实表的一般设计过程。
第一步,选择业务过程。
交易订单的流转,介绍了四个重要过程:创建订单、买家付款、卖家发货、买家确认收货,即下单、支付、发货和成功完结四个业务过程。这四个业务过程不仅是交易过程中的重要时间节点,同时也是下游统计分析的重点,因此交易事务事实表着重从这四个业务过程进行展开。Kimball 维度建模理论认为,为了便于独立的分析研究,应该为每一个业务过程建立一个事实表,对于不同业务过程是否放到同一个事实表中,将在下一节详细介绍。
第二步,确定粒度。
业务过程选定以后,就要针对每个业务过程确定一个粒度,即确定事务事实表每一行所表达的细节层次,这里先介绍下订单过程。电商平台出售的商品主要分两类卖家,一类是个人性质的闲置卖家,主要出售闲置或者二手商品,另外一类是拥有店铺类的卖家,以出售新商品为主。接下来主要以店铺类的交易订单为主进行介绍。
在下单交易时,有两种方式:一种是选定商品然后直接购买,这样会产生一条交易订单;另外一种是将多件商品加入到购物车,然后从购物车一起结算,此时对于每一件商品都会产生一个订单,同时对于同一个店铺会额外产生一个订单,即父订单,由于是同一个店铺购买,所以此时父订单会承载订单物流、店铺优惠等信息。而对于每一个商品产生的订单就称为子订单,子订单记录了父订单的订单号,并且有子订单标记。如果在同一个店铺只购买了一件商品则会将父子订单进行合并,只保留一条订单记录。如下所示,分别是父子订单合并一条和父子订单合并多条。
了解了交易订单的产生过程后,接下来将为交易事务事实表确定粒度。如第一步所述,交易过程中有四个重要业务过程,需要为每一个业务过程确定一个粒度。其中下单、支付和成功完结三个业务过程选择交易子订单粒度,即每一笔子订单为事务事实表的一行,每一笔子订单所表达的细节信息:交易时间、卖家、买家、商品,即选择上表中订单 ID 为 1、4、5、6、7、9 的子订单作为事务事实表的每一行。
卖家发货这个业务过程可以选择子订单粒度,即将每一笔子订单作为卖家发货事实表的一个细节,然而在实际操作中发现,卖家发货更多是物流单粒度而非子订单粒度,同一笔子订单可以拆开多笔物流进行发货,在事务事实表设计过程中,秉承确定粒度为最细粒度,因此对于卖家发货的粒度确定为物流单粒度,和其他三个业务过程不同,这样可以更好的给下游统计分析带来灵活性。
第三步,确定维度。
对于选定的业务过程并且确定粒度后,就可以确定维度信息。交易事务事实表在设计过程中,按照经常用于统计分析的场景,确定维度包含:买家维度、卖家维度、商品维度、商品类目维度、发货地区维度、收货地区维度、父订单维度以及杂项维度。由于订单的属性较多,比如订单的业务类型、是否无线交易、订单属性等等,这些使用较多却又无法归属到上述买卖家或商品维度中则新建一个杂项维度进行存放。如下图所示。
第四步,确定事实。
作为过程度量的核心,事实表应该包含与其描述过程有关的所有事实。以交易事务事实表为例,选定三个业务过程下单、支付和成功完结,不同的业务过程拥有不同的事实。比如下单业务过程中,需要包含下单金额、下单数量、下单分摊金额,支付业务过程中,包含支付金额、分摊邮费、折扣金额、红包金额、积分金额,完结业务过程中包含确认收货金额等,由于粒度是子订单的,所以对于一些父订单上的金额需要平摊到子订单上,比如父订单邮费、父订单折扣等,具体分摊算法将在父子事实处理方式一节介绍。根据 Kimball 维度建模理论,经过以上四步,交易事务事实表已初见成型,可以满足下游分析统计需要,然而实际数据仓库在建模时,基于以上四个过程增加了一步——退化维度,这个过程在 Kimball 维度建模中也有所提及,基于效率和资源考虑将常用维度全部退化到事实表中,使下游分析使用模型更加方便。
第五步,冗余维度。
第三步确定维度时,包含了买家和卖家维度、商品维度、类目维度、收发货维度等等,Kimball 维度建模建议在事实表中只保存这些维度表的外键,而交易事务事实表在 Kimball 维度建模基础之上做了进一步的优化,将买家和卖家的星级、标签、店铺名称、商品类型、商品特征、商品属性、类目层级等等维度属性都冗余到事实表中,提高对事实表过滤查询、统计聚合的效率,如下图所示。
经过以上五个步骤,完成了交易事务事实表的设计。但设计过程中遗留的一个问题,即单一事实表中是否包含多个业务过程,还没有给出定论,接下来将通过对比淘宝和 1688 交易过程中不同的设计方案来阐述两种设计方法。
2.2.2 单事务事实表
单事务事实表,顾名思义,即针对每一个业务过程设计一个事实表,这样设计的优点不言而喻,可以方便的对每一个业务过程进行独立的分析研究。1688交易流程则采用了这种模式构建了事务事实表。
1688 交易和淘宝交易相似,主要流程也是下单、支付、发货和完结,而在这四个关键流程中 1688 交易选择下单和支付两个业务过程设计事务事实表,分别是 1688 交易订单创建事务事实表和 1688 交易订单支付事务事实表。
选定业务过程后,将对每一个业务过程确定粒度、维度和事实。对于 1688交易订单创建事务事实表,确定子订单粒度,选择买家、卖家、商品、父订单、收货地区维度,事实包含下单分摊金额和折扣金额;而对于 1688 交易订单支付事务事实表,粒度和维度和交易订单创建(下单)事务事实表相同,所表达的事实则不一样,包含支付金额、支付调整金额和支付优惠等,如下图所示。
1688 交易针对下单和支付分别建立单事务事实表后,每天的下单记录则进入当天的下单事务事实表中,每天的支付记录进入当天的支付事务事实表中,由于事实表拥有稀疏性质,因此只有当天有数据才会进入当天的事实表中。下面以一条交易订单为例,展示单事务事实表的数据流转情况。
order1 在 2024-01-01 下单并且在当天完成支付;order3 和 order4 在2024-01-01 下单并且在 2024-01-02 完成支付。
业务库表:交易详情实例
事实表:1688 交易订单创建事务事实表数据实例
事实表:1688 交易订单支付事务事实表数据实例
2.2.3 多事务事实表
多事务事实表,将不同的事实放到同一个事实表中,即同一个事实表包含不同的业务过程。多事务事实表在设计时有两种方法进行事实的处理:
- 不同业务过程的事实使用不同的事实字段进行存放;
- 不同业务过程的事实使用同一个事实字段进行存放,但增加一个业务过程标签。接下来将分别通过交易事务事实表和收藏事务事实表分别阐述设计方法。
第一种:
交易事务事实表(见 2.2.1 节业务库表:交易详情实例)
第二种:
收藏事务事实表
收藏和加购物车是购物过程中比较常见的两个行为,当用户遇到喜欢的商品或者店铺时可以选择收藏然后下次继续浏览购买。这里以收藏事务事实表阐述多事务事实表在处理不同业务过程使用同一个字段保存事实的设计方式。
收藏业务较为简单,商品和店铺的收藏业务相似,这里仅以收藏商品为例进行阐述。用户可以直接收藏一个商品,收藏后可以删除收藏的这个商品,所以在这个过程中包含了两个业务过程:收藏商品和删除商品。
因此收藏商品事务事实表的第一步选择业务过程,就选定收藏商品和删除商品两个业务过程。业务过程确定后,就是确定粒度。无论是收藏商品还是删除收藏的商品,都是用户对商品的一个操作,因此这里确定用户加上商品的粒度。
业务过程和粒度确定好后,接下来是确定维度和事实。由于粒度是用户加上商品,所以维度主要是用户维度和商品维度,为了使事实表信息更丰富,冗余了商品类目维度和商品所属卖家维度,收藏商品和删除商品业务过程所属维度都是一致的。
下面通过一组例子来说明收藏事务事实表的设计过程。
- 业务库表:收藏商品明细表
- 收藏事务事实表
多事务事实表的选择:
上面介绍了两种多事务事实表的设计方式,在实际应用中选择哪一类需要因业务过程不同而不同。由于是多事务事实表,因此在事实表中包含多个业务过程:
- 当不同业务过程的度量比较相似、差异不大时则采用第二种多事务事实表的设计方式,使用同一个字段来表示度量数据,但这种方式面对一个问题——同一个周期内会存在多条记录;
- 当不同业务过程的度量差异较大时,可以选择第一种多事务事实表的设计方式,将不同业务过程的度量使用不同字段冗余到表中,非当前业务过程则置零表示,问题是度量字段零值较多。
2.2.4 两类事实比较
前面介绍了单事务事实表和多事务事实表的设计过程,同时给出了 1688 和淘宝关于不同事务事实表的实例。目前两类事实表都有实际的应用,但具体哪一种设计方式更优,我们接下来做一个分析。
- 业务过程
单事务事实表对一个业务过程建立一张事实表,只反映一个业务过程的事实;多事务事实表在同一张事实表中反映多个业务过程。多个业务过程是否放到同一个事实表中,首先需要分析不同业务过程之间的相似性和业务源系统,比如交易的下单、支付和成功完结这三个业务过程是存在相似性的,都是属于订单处理中的一环,并且都是来自于交易系统,因此适合放到同一个事务事实表中。 - 粒度和维度
在考虑单事务事实表还是多事务事实表,另一个关键点就是粒度和维度,在确定好几个业务过程后,需要基于不同业务过程确定粒度和维度,当不同业务过程的粒度相同,同时拥有相似的维度,此时就可以考虑多事务事实表方式。如果粒度不同,则必定是不同的事实表,比如交易中支付和发货则不属于同一粒度,无法将发货业务过程放到交易事务事实表中。 - 事实
对于不同的业务过程,事实往往是不同的,单事务事实表在处理事实上比较方便和灵活,仅仅体现同一个业务过程的事实即可。而多事务事实表由于有多个业务过程,所以有更多的事实需要处理,如果单一业务过程的事实较多,同时不同业务过程的事实又不相同,则可以考虑使用单事务事实表,处理更加清晰,若使用多事务事实表则会导致事实表零值或空值字段较多。 - 下游业务使用
单事务事实表于下游用户而言更容易理解,关注哪个业务过程则使用相应的事务事实表;而多事务事实表包含多个业务过程,用户使用时往往较为困惑,下游使用有一定的学习成本。 - 计算存储成本
针对多个业务过程设计事务事实表,采用单事务还是多事务,对于数据仓库的计算存储成本也是参考点之一,当业务过程数据来源于同一个业务系统,具有相同的粒度和维度,另外维度较多而事实相对不多,此时可以考虑使用多事务事实表,不仅加工计算成本较低,同时存储上也相对节省,此时多事务事实表是一个较优的处理方式。
2.2.5 父子事实的处理方式
交易父子订单的含义在前文确定粒度时有所说明,在同一家店铺同时下单的多件商品,不仅每件商品有一个子订单,这几个子订单会再单独产生一个父订单。
下单和支付都是在父订单粒度上完成的,比如拍下时的订单总额、支付的总额、支付的邮费,交易事务事实表在粒度选取上,按照粒度最细原则,确定为子订单,因此需要将下单总额或者支付总额分摊到每个子订单上,当然只有一个子订单时
是不需要进行分摊的。这里以子订单分摊的有效下单金额和子订单分摊的支付金额加以说明。
- 子订单下单金额=下单商品数量*商品价格
- 子订单分摊有效下单金额=下单商品数量*商品价格+父订单邮费*下单分摊比例-子订单折扣-父订单折扣*下单分摊比例
- 下单分摊比例=(下单商品数量*原价-子订单折扣)/sum(下单商品数量*原价子订单折扣)
- 子订单分摊的支付金额=父订单支付金额*支付分摊比例
- 支付分摊比例=(下单商品数量*原价-子订单折扣+调价)/sum(下单商品数量*原价-子订单折扣+调价)
通过分摊父订单的金额将所有业务过程的度量全部带进交易事务事实表中,包括了下单数量、商品价格、子订单折扣、下单分摊比例、父订单支付金额、父订单支付邮费、父订单折扣、子订单下单金额、子订单下单有效金额、支付分摊比例、子订单支付金额等等,将父子事实同时冗余到事务表中。
2.2.6 事实的设计原则
- 事实完整性
事实表包含与其描述的过程有关的所有事实,即尽可能多的获取所有的度量。在交易事务事实表中,比如支付业务过程,子订单粒度上的支付金额、支付邮费、支付红包、支付积分、支付折扣,都有所包含,覆盖全面。 - 事实一致性
在确定事务事实表的事实时,明确的存储每一个事实以确保度量的一致性。以交易事务事实表为例,下单业务过程中,有下单商品数量和商品价格两个事实,但在事实表中计算了下单金额和下单有效金额,这些可以通过商品数量乘以商品价格进行计算,虽然下游在取数时也可以通过这种方式完成计算,但是在事实表中统一计算可以保证这个度量的一致性,其他如支付过程中的分摊金额等也是类似的。 - 事实可加性
事实表确定事实时,往往会遇到非可加性的度量,比如分摊比例、利润率等,这些虽然也是下游分析的关键点,但往往在事务事实表中更多是关注可加性的事实,下游用户在聚合统计时更加方便。在交易事务事实表中,存储了分摊比例这样的度量,但更多的是存储各类金额的度量。
2.3 周期快照事实表
当需要一些状态度量时,比如账户余额、买卖家星级、商品库存、卖家累积交易额等状态量,需要聚集与之相关的事务才能进行识别计算;或者聚集事务无法识别,比如温度等。对于这些状态度量,事务事实表是无效率的,而这些度量也是和度量事务本身一样是有用的。
因此,维度建模理论给出了第二种常见的事实表——周期性快照事实表,或者简称为快照事实表。快照事实表在确定的间隔内对实体的度量进行抽样,这样可以很容易的研究实体的度量值,而不需要聚集长期的事务历史。接下来将以订单交易结束后的评价数据、卖家的累积支付金额、买卖家星级等事实表的设计来介绍快照事实表的设计与应用。
2.3.1 快照事实表特性
首先简要介绍下快照事实表的一些特性,快照事实表的设计有一些区别于事务事实表设计的性质。事务事实表的粒度能以多种方式表达,但快照事实表的粒度通常以维度形式声明。事务事实表是稀疏的,但快照事实表是稠密的。事务事实表中的事实是完全可加的,但快照模型将包含至少一个用来展示半可加性质的事实。
用快照采样状态
快照事实表以预定的间隔采样状态度量。这种间隔联合一个或多个维度,将被用来定义快照事实表的粒度,每行将包含记录所涉及状态的事实。这里以电商平台交易卖家自然年汇总事实表为例进行介绍,对于活动运营小二或者卖家自身经常都需要看一些交易状态数据,比如自然年至今或者历史至今的下单金额、支付金额、支付买家数、支付商品件数等等状态度量;于卖家而言,可能是每天早上都想看一下截至昨天的一个成交情况。于小二而言,可能频繁的活动周期就需要查看一次成交情况,这些状态度量
可以每天通过事务事实表进行聚集,但随着时间跨度变大,聚集效率会越来越低,因此设计事实表快照进行状态的度量,这里用于采样的周期间隔就是每天,如下图所示的快照事实表记录了每个卖家的下单和支付情况。
快照粒度
事务事实表的粒度可以通过业务过程中涉及到的细节程度来描述,但快照事实表的粒度通常总是被多个维度来声明,可以简单理解为快照需要采样的周期以及什么将被采样。淘宝交易卖家事实表快照中,粒度可以理解为每天针对卖家的历史截至当日下单支付金额进行快照。
当然快照周期并不一定都是按天来进行的。可以按照周、月或者季度来统计。
比如交易有针对【卖家+类目】的每月汇总事实表,每月统计一次,同时维度也不仅一个,包含卖家和类目。密度与稀疏性
快照和事务事实表的一个关键区别也在于密度上。事务事实表是稀疏的,只有当天发生了相应的业务过程,事实表才会记录该业务过程的事实,如下单、支付等等;而快照是稠密的,无论当天是否有业务过程发生,都会记录一行,比如针对卖家的历史至今的下单和支付金额,无论当天卖家是否有下单支付事实,都会给该卖家记录一行。稠密性是快照事实表的重要特征,如果在每个快照周期内不记录行,比如和事务事实表一样,确定状态将变得非常困难。半可加性
快照事实表中收集到的状态度量都是半可加的。与事务事实表的可加事实不同,半可加事实不能根据时间维度获得有意义的汇总结果。比如对于交易事务事实表,可以对一段周期内的下单金额或者支付金额进行汇总,得到一段周期内的下单支付总额,但快照事实表在每个采样周期内是不能对状态度量进行汇总的,比如淘宝交易卖家快照事实表,无法对每天的历史至今的下单金额进行汇总,也没有汇总意义。虽然不能汇总,但可以计算一些平均值,比如计算每天的一个下单均值。
2.3.2 快照事实表实例
针对不同的业务场景,事务事实表无法满足所有需求,正如上一小节介绍,在统计历史至今的卖家或者类目的下单金额和支付子订单数时,通过事务事实表聚集效率较低,而周期快照事实表则是可行的方案。接下来主要介绍数据仓库几类周期快照事实表的设计过程。
通过前一节快照事实表的特性介绍,对于快照事实的设计步骤可以归纳为:
(1)确定快照事实的快照粒度;
(2)确定快照事实表采样的状态度量;
以下将依照这个步骤介绍几类常见的快照事实表。
单维度的每天快照事实表
第一步:确定粒度。
采样周期为每天,针对卖家、买家、商品、类目、地区等维度的快照事实表。
如卖家历史至今汇总事实表、商品自然月至今汇总事实表等,不同的采样粒度确定了不同的快照事实表。第二步:确定状态度量。
确定好粒度以后,就要针对这个粒度确定需要采样的状态度量。比如卖家历史至今汇总事实表,包含了历史截至当日的下单金额、历史截至当日的支付金额等等度量。- 卖家历史至今快照事实表,如下图所示。
- 商品历史至今快照事实表,确定商品维度和商品状态度量,如下图所示
- 卖家历史至今快照事实表,如下图所示。
混合维度的每天快照事实表
混合维度相对于单维度,只是在每天的采样周期上针对多个维度进行采样。比如交易买卖家截至当日快照事实表,采样周期依然是每天,维度是【卖家+买家】,反映的是不同买家对于不同卖家的下单支付金额。- 买卖家历史至今快照事实表,如下图所示。
- 买卖家历史至今快照事实表,如下图所示。
以上两类快照事实表都有一个特点——都可以从事务事实表进行汇总产出,这是周期快照事实表常见的一种产出模式,除此以外还有另外一种产出模式,即直接使用操作型系统的数据作为周期快照事实表的数据源进行加工,比如淘宝卖家星级、卖家 DSR 事实表都是类似的。
全量事实表快照
数据仓库在设计快照事实表时,还有一类特殊的快照事实表,即全量型事实表快照,这类事实表相对于前面所述的快照事实表特性有一些差异,但依然是周期快照事实表范畴,下面还是以淘宝好中差评价快照事实表阐述该类事实表的设计方法。
第一步,确定粒度。淘宝好中差评是每天都在变化,下游统计分析也是每天进行的,因此确定采样周期是每天,这里的采样维度比较特殊,是针对评价本身,即每天按照评价进行采样,每一条好中差评价就是快照事实表的最细粒度。第二步,确定状态度量。对于好中差评价的度量更多关注的是评价本身,即没有类似金额、商品数这样的度量,因此设计为无事实的事实表,更多是关注评价的状态。
对于全量事实表快照,这里再增加一步,即冗余维度。如好中差评价快照事实表,冗余了子订单维度、商品维度、评论者维度、被评论者维度以及杂项维度,包括评论内容、是否匿名等信息。
- 淘宝好中差评快照事实表,如下图所示。
- 淘宝好中差评快照事实表,如下图所示。
2.3.3 快照事实表注意事项
- 事务与快照成对设计
实际企业数据仓库进行维度建模时,往往对于事务事实表和快照事实表都是成对的设计,进行互相补充,以满足更多的下游统计分析,特别在于,在事务事实表的基础上可以加工快照事实表,如 2.3.2 节所述的卖家历史至今的快照事实表,就是在事务事实表的基础上加工得到,既丰富了星型模型,又减轻了下游分析的成本。 - 附加事实
快照事实表在确定状态度量时,一般都是保存采样周期结束时的状态度量,但是往往也有分析需求需要关注前一个采样周期结束时的状态度量,而又不愿意多次使用快照事实表,因此一般在设计周期快照事实表时,会附加一些上一采样周期的状态度量。 - 周期到日期度量
2.3.2 节介绍卖家历史至今的快照事实表时,指定了统计周期是卖家历史至今的一些状态度量,比如历史截至当日的下单金额、成交金额等等,然后在实际应用中,也有需要关注自然年至今、季度至今、财年至今的一些状态度量,因此在周期快照事实表的度量确定时,也要考虑类似的度量值,以满足更多的统计分析需求。在设计周期快照事实表时,就针对多种周期到日期的度量设计了不同的快照事实表,比如卖家财年至今的下单金额、商品自然年至今的收藏次数等等。
2.4 累积快照事实表
针对交易,设计了交易下单/支付/确认收货事务型事实表,用于统计下单 OR支付 OR 确认收货的子订单数、GMV 等。但仍然有很多需求,此事务型事实表很难满足。比如统计买家下单到支付的时长、买家支付到卖家发货的时长、买家从下单到确认收货的时长等。如果使用事务型事实表统计,逻辑复杂且性能很差。对于类似研究事件之间时间间隔的需求,采用累积快照事实表可以很好的解决。
2.4.1 累积快照事实表设计过程
对于累积快照事实表,建模过程和事务型事实表相同,适用维度建模的步骤。
下面详述一下交易累积快照事实表的设计过程,并对比事务型事实表的设计差
异。
- 第一步,选择业务过程。
2.1 事实表基础章节讲解了交易订单的流转过程,主要有如下四个业务过程:买家下单、买家支付、卖家发货、买家确认收货。对于此 4 个业务过程,在事务型统计中只关注下单、支付和确认收货三个业务过程。而在统计事件时间间隔的需求中,卖家发货也是关键环节。所以针对交易累积快照事实表,我们选择此四个业务过程。 - 第二步,确定粒度。
在事务型事实表中提到,对于交易,业务需求一般是从子订单粒度进行统计分析,所以选择子订单粒度。交易事务型事实表的粒度也是子订单,但通常对于子订单的每个事件都会记录一行,对于多事件事实表如果子订单同一周期发生多次事件则记录一行;而对于累积快照表,用于考察实体的唯一实例,所以子订单在此表中只有一行记录,事件发生时,对此实例进行更新。 - 第三步,确定维度。
同事务型事实表相同,维度主要有买家、卖家、店铺、商品、类目、发货地区、收货地区等。四个业务过程对应的时间字段,格式为日期+时间,分别为下单时间、支付时间、发货时间、确认收货时间,会对应日期维度表,下图中未标示。实际使用时会使用视图或 SQL 别名的方式表示四个日期角色维度。类似于发货地区维度和收货地区维度。
交易订单表中,存在很多订单相关的属性,如订单的类型、子类型、支付状态、物流状态、attributes、options 等。对于类似的属性字段,无法归属到已有的商品等维度中,所以新建杂项维度存放。在数据仓库建模理论中,杂项维度一般无自然键,一般是可枚举值的组合,对于每个组合生成一个代理键。但实际建模中,存在很多不可枚举值,且对于每个订单都不相同,如订单的 attributes 和 options 属性。所以实际中杂项维度设计时,也可以直接使用自然键标示具体的维度值,如下图中所示的子订单维度和父订单维度。
2.4.2 累积快照事实表特点
数据不断更新
事务型事实表记录事务发生时的状态,对于实体的某一实例不再更新;而累积快照事实表则对实体的某一实例定期更新。多个业务过程日期
通过上面的实例,可以看到累积快照事实表典型特征:多个业务过程日期。累积快照适用于具有较明确起止时间的短生命周期的实体。比如交易订单、物流订单等,对于实体的每一个实例,都会经历从诞生到消亡等一系列步骤。对于商品、用户等具有长生命周期的实体,一般采用周期性快照事实表更合适。
累积快照事实表典型特征是多个业务过程日期,用于计算业务过程之间的时间间隔。但结合数据仓库模型建设的经验,对于此表,还有一个重要作用是保存全量数据。对于交易,需要保留历史截止当前的所有交易数据,其中一种方式是在 ODS 层保留和源系统结构完全相同的数据;但由于使用时需要关联维度,较为麻烦。所以在公共明细层,需要保留一份全量数据,交易累积快照事实表就承担了这样的作用。存放了加工后的事实,并将各维度常用属性和订单杂项维度退化到此表。常用于数据探查、统计分析、数据挖掘等。
2.5 三种事实表的区别
(☆☆☆重点记忆)
通过前面的章节,对数据仓库三种事实表有了详细的理解。一些业务过程可能只需要一种事实表,但另外一些过程可能需要两种或三种事实表。三种事实表相互补充,给出业务的完整描述。
- 事务事实表:记录的事务层面的事实,用于跟踪业务过程的行为,并支持几种描述行为的事实,保存的是最原子的数据,也称“原子事实表”。事务事实表中的数据在事务事件发生后产生,数据的粒度通常是每个事务一条记录。一旦事务被提交,事实表数据被插入,数据就不再进行更改,其更新方式为增量更新。
- 周期快照事实表:以具有规律性的、可预见的时间间隔来记录事实,如余额、库存、层级、温度等,时间间隔如每天、每月、每年等等。典型的例子如库存日快照表等。周期快照事实表的日期维度通常是记录时间段的终止日,记录的事实是这个时间段内一些聚集事实值或状态度量。事实表的数据一旦插入即不能更改,其更新方式为增量更新。
- 累积快照事实表:被用来跟踪实体的一系列业务过程的进展情况,它通常具有多个日期字段,用于研究业务过程中的里程碑过程的时间间隔。另外,它还会有一个用于指示最后更新日期的附加日期字段。由于事实表中许多日期在首次加载时是不知道的,而且这类事实表在数据加载完后,是可以对它进行更新的,来补充业务状态变更时的日期信息和事实。
2.6 无事实的事实表
维度模型中,事实表用事实来度量业务过程,不包含事实或度量的事实表称为无事实的事实表。虽然没有明确的事实,却可以用来支持业务过程的度量。
常见的无事实的事实表主要有如下两种:
第一种,事件类,记录事件的发生。在数据仓库中,最常见的是日志类事实表。比如用户的浏览日志,某会员某时间点浏览了首页、某会员某时间点浏览了某卖家的店铺中的某商品详情页等。对于每次点击,其事实为 1,但一般不会保存此事实。
第二种,条件、范围或资格类,记录维度与维度多对多之间的关系。比如客户和销售人员的分配情况、产品的促销范围、优惠工具的圈品情况等。
3. 维度表设计
3.1 基本概念
维度是维度建模的基础和灵魂。维度建模中,将度量称为“事实”,将环境描述为“维度”,维度是用于分析事实所需要的多样环境。例如,在分析交易过程时,可以通过买家、卖家、商品和时间等维度描述交易发生的环境。
维度所包含的表示维度的列,称为维度属性。维度属性是查询约束条件、分组和报表标签生成的基本来源,是数据易用性的关键。例如在查询请求中,我们取某类目的商品,取正常状态的商品等,是通过约束商品类目属性和商品状态属性来实现的;例如,我们统计不同商品类目每日成交金额,是通过商品维度的类目属性进行分组的;例如,我们在报表中看到的类目等,都是维度属性。所以维度的作用一般有以下几点,查询约束、分类汇总以及排序等。
如何获取维度或它们的属性?如上面提到的,一方面,可以在报表中获取;另一方面,可以在和业务人员的交谈中能够发现维度或它们的属性。因为它们经常出现在查询或报表请求中的"按照"(by)语句内。例如,用户需要"按照"月份和产品来查看销售情况。用户描述其业务的自然方法应该作为维度或维度属性包括在维度模型中。
维度使用主键标示其唯一性,主键也是确保与之相连的任何事实表之间存在引用完整性的基础。主键有两种方式:代理键和自然键,它们都是用于表示某维度的具体值。但代理键是不具有业务含义的键,一般用于处理缓慢变化维度;自然键是具有业务含义的键。如商品,在 ETL 过程中,对商品维表的每一行,可以生成一个唯一的代理键与之对应;商品本身的自然键可能是商品 ID 等。其实对于前台应用系统,商品 ID 是代理键;
3.2 设计方法
维度的设计过程就是确定维度属性的过程,如何生成维度属性、生成的维度属性的优劣,决定了维度使用的方便性,成为数据仓库易用性的关键。正如 Kimball 所说,数据仓库的能力直接与维度属性的质量和深度成正比。下面我们用商品维度表的设计为例对维度设计的方法进行详细说明。
- 选择维度或者新建维度:在建设维度表中,要保证其在数仓中的唯一性,也就是说只允许有一个商品维表。
- 确定维度主来源表:在此处一般指的就是 ODS 层(与业务系统表结构一样)的商品表,如 s_items_info,此表就是维度的主来源表。
- 确定相关维表:数据仓库的设计遵循数据的高度整合原则。在确定主来源表后,还需要根据实际需求,扩展商品的相关信息如:类目、所属卖家、所属店铺等。
- 确定维度属性:本步骤主要包括两个阶段,第一阶段是主维度表中选择维度属性或生成新的维度属性;第二阶段是从相关维度表中选择维度属性或生成新的维度属性。以淘宝商品维度为例。以淘宝商品维度为例,从主维度表(s_auction_auctions)和类目、SPU、卖家、店铺等相关维度表选择维度属性或生成新的维度属性。
确定维度属性的几点 TIPS:
- 维度属性的作用和重要性已经多次提及,尽可能生成丰富的维度属性。比如淘宝商品维度约有近百个维度属性,为下游的数据统计、分析、探查提供了良好的基础。
- 尽可能多给出包括一些富有意义的文字性描述,属性不应该是编码而应该是真正的文字。维度建模中,一般是编码和文字同时存在,比如商品维度中的商品 ID 和商品标题,类目 ID 和类目名称等。ID 一般用于不同表之前的关联,而名称一般用于报表标签。
- 区分数值型属性和事实。数值型字段是作为事实还是维度属性,可以参考字段的一般用途。如果通常是用于查询约束条件或分组统计,则是作为维度属性;如果通常是用于参与度量的计算,则是作为事实。比如商品价格,可以用于查询约束条件或统计价格区间的商品数量,此时是作为维度属性使用;也可以用于统计某类目下商品的平均价格,此时是作为事实使用。另一方面,如果数值型字段是离散值,则作为维度属性存在的可能性较大;如果数值型字段是连续值,则作为度量存在的可能性较大,但并不绝对,需要同时参考字段的具体用途。
- 尽量沉淀出通用的维度属性 有些维度属性获取需要进行比较复杂的逻辑处理,有需要通过多表关联得到,也有单表的不同字段混合处理得到,或者对单表的某个字段进行解析得到。此时,需要将尽可能多的通用的维度属性进行沉淀。一方面,可以提高下游使用的方便性,减少复杂度;另一方面,避免下游使用解析时由于各自逻辑不同而导致的口径不一致。例如,淘宝商品的 property 字段,使用 key:value 方式存储多个商品
属性。商品品牌就存存储在此字段中,而商品品牌是重要的分组统计和查询约束的条件,所以需要将品牌解析出来,作为品牌属性存在。例如,商品是否在线,即淘宝网站是否可以查看到此商品,是重要的查询约束的条件,但是无法直接获取需要进行加工,加工逻辑是:商品状态 0 和 1 且商品上架时间小于等于当前时间,则是在线商品,否则是非在线商品。所以需要封装商品是否在线的逻辑作为一个单独的属性字段。
3.3 维度的层次结构
维度中的一些描述属性以层次方式或一对多的方式相互关联,可以被理解为包含连续主从关系的属性层次。层次的最底层代表维度中描述最低级别的详细信息,最高层次代表最高级别的概要信息。维度常常有多个这样的嵌入式层次结构。比如淘宝商品维度,有卖家、类目、品牌等。商品属于类目、类目属于行业,其中类目的最低级别是叶子类目、叶子类目属于二级类目、二级类目属于一级类目。
在属性的层次结构中进行钻取是数据钻取的方法之一。数据钻取分为上卷(维度减少)和下钻(维度增多)。简单来说就是想点开年份看详细的月份或者天数据,就叫下钻;如果由每天的维度变为看季度、年维度,那就是上卷。常见的维度层次结构有以下几个:日期,地址,类目等。通过具体的例子,我们来看如何在层次结构中进行钻取。
假设我们已有一个淘宝交易订单创建事实表。现在统计 2023 年双 11 的下单GMV,我们得到一行记录;沿着层次向下钻取,添加行业,得到行业实例个数的记录数;继续沿着层次向下钻取,添加一级类目,得到一级类目实例个数的记录数。可以看到,通过向报表中添加连续的维度细节级别实现在层次结构中进行钻取。
最高层次的统计:
钻取至行业层次:
日期 行业 一级类目 下单 GMV
20231111 行业 1 ALL industry1_gmv
20231111 行业 2 ALL industry2_gmv
20231111 … … …
20231111 行业 N ALL industryN_gmv钻取至一级类目层次:
3.4 规范化和反规范化
当属性层次被实例化为一系列维度,而不是单一的维度时,此模式被称为雪花模式。大多数联机事务处理系统(OLTP)的底层数据结构在设计时采用此种规范化技术,通过规范化处理将重复属性移至其自身所属的表中,删除冗余数据。
此种方法用在 OLTP 系统中可以有效避免数据冗余导致的不一致性。比如在OLTP 系统中,存在商品表和类目表,且商品表中冗余有类目表的属性字段,假设对某类目进行更新,则必须更新商品表和类目表,且由于类目和商品是一对多的关系,商品表可能每次需要更新几十万甚至上百万条记录,这是不合理的。而对于联机分析处理系统(OLAP),数据是稳定的,不存在 OLTP 系统中存在的问题。
对于淘系商品维度,如果采用雪花模式进行规范化处理,将表现为如下形式:
将维度的属性层次合并到单个维度中的操作称为反规范化。分析系统的主要目的是用于数据分析和统计,如何更方便用户进行统计分析决定了分析系统的优劣。采用雪花模式,用户在统计分析的过程中需要大量的关联操作,使用复杂度高,同时查询性能很差;采用反规范化处理,方便易用且性能好。
如上所述,从用户的角度来看这简化了模型,并且使数据库查询优化器的连接路径比完全规范化的模型简化许多。反规范化的维度仍包含与规范化模型同样的信息和关系,从分析角度来看,没有丢失任何信息,但复杂性降低了。
采用雪花模式,除了可以节约一部分存储,对于 OLAP 系统来说没有其它效用。而现阶段存储的成本非常低。基于易用性和性能考虑,维度表一般是很不规范化的。实际应用中,几乎总是使用维度表的空间来换取简明性和查询性能。
3.5 一致性维度和交叉检查
构建企业级的数据仓库不可能一蹴而就,一般采用迭代式的构建过程。但单独构建存在的问题是形成独立型数据集市,导致严重的不一致性。Kimball 的数据仓库总线架构提供了一种分解企业级数据仓库规划任务的合理方法。通过构建企业范围内一致性的维度和事实来构建总线架构。
数据仓库总线架构重要基石之一就是一致性维度。在针对不同数据域进行迭代构建或并行构建时,存在很多需求是对于不同数据域的业务过程或者同一数据域的不同业务过程合并在一起观察;比如对于日志数据域,我们统计了商品维度的最近一天访问 PV 和 UV 数;对于交易数据域,我们统计了商品维度的最近一天的下单 GMV。现在我们将不同数据域的商品的事实合并在一起进行数据探查,如计算 UV 转化率等,我们称为交叉探查。
如果不同数据域计算过程使用的维度不一致,就会导致交叉探查存在问题。当存在重复的维度,但维度属性或维度属性的值不一致,会导致交叉探查无法进行或交叉探查结果错误。
如上所示,假设日志数据域统计商品维度的最近一天 PV 和 UV 使用的商品维度 1,交易数据域统计商品维度使用的是商品维度 2。
- 商品维度 1 包含维度属性 BC 类型,而商品维度 2 无此属性,则无法在 BC 类型上进行交叉探查;
- 商品维度 1 商品上架时间这一维度属性时间格式是字符串格式 yyyy-MM-dd HH:mm:ss,商品维度 2 商品上架时间这一维度属性时间格式是 Unixtimestamp,进行交叉探查时如果需要根据商品上架时间做限制,则复杂性较高;
- 商品维度 1 不包含阿里旅行的商品,商品维度 2 包含全部淘系商品,探查也无法进行;还有很多种形式的不一致,不再一一列举,基本可以划分维度格式和内容的不一致这两种类型。
如何才能保证有一致性维度呢?有三种方法:
- 共享维度表。比如在阿里巴巴的数据仓库中,商品、卖家、买家、类目等维度,有且只有一个。所以基于这些公共维度进行的交叉探查,不会存在任何问题。
- 一致性上卷。其中一个维度的维度属性是另一个维度的维度属性的子集,且两个维度的公共维度属性结构和内容相同。比如商品维度和类目维度,其中类目维度的维度属性是商品维度的维度属性的子集,且有相同的维度属性和维度属性值。这样基于类目维度进行不同业务过程的交叉探查也不会存在任何问题。
- 交叉属性。两个维度具有部分相同的维度属性。比如在商品维度中具有类目属性,在卖家维度中具有主营类目属性,两个维度具有相同的类目属性,则可以在相同的类目属性上进行不同业务过程的交叉探查。
4. 维度设计高级主题
集成性是数据仓库的四个特性中最重要的一点,维度设计中需要考虑如何集成来自应用系统大量分散的操作型环境的数据。
维度有两种拆分方式,水平拆分和垂直拆分。水平拆分通常基于维度类别或类型进行细分。垂直拆分通常基于扩展性、产出时间、易用性等方面的进行考虑。主维度表存放稳定、产出时间早、热度高的属性;从维度表存放变化较快、产出时间晚、热度低的属性。
截止当前的淘系商品约有几百亿的记录,在数据仓库占用约 18T 的存储。面对如此庞大的数据量,如何设计模型、如何降低存储、如何让下游方便获取数据,成为必须要解决的问题。数据归档是解决此问题的有效方法之一。
4.1 维度整合
我们先来看下数据仓库的定义:数据仓库是一个面向主题的、集成的、相对稳定的且反映历史变化的数据集合,用来支持管理人员的决策。其中集成是数据仓库的四个特性中最重要的一点。
数据仓库的重要数据来源是大量的、分散的面向应用的操作型环境。不同的应用在设计过程中,可以自由决策,主要满足本应用的需求,很少会考虑和其它系统进行数据集成。应用的差异具体表现在如下几个方面:
- 应用在编码、命名习惯、度量单位等方面会存在很大差异。比如不同应用对于用户的性别编码不同:有 0 和 1,有 F 和 M 等;不同应用用户 ID 含义相同但字段名称不同:有 user,有 uid、user_id、buyer_id 等;不同应用对于金额的度量单位不同:有元,有分等等。
- 应用基于性能和扩展性的考虑,或者随技术架构的演变,或者随业务的发展,采用不同的物理实现。拆分至不同类型数据库,部分数据采用关系型数据库存储(如 Oracle、Mysql 等),部分数据采用 NoSQL 数据库存储(如 Hbase、Tair 等)。拆分成同一类型数据库中多张的物理表,比如淘宝商品,有商品主表和商品扩展表,商品主表存商品基本信息;商品扩展表存储商品特殊信息,如不同产品线定制化的信息等;比如淘宝会员,有会员主表和会员扩展表,会员主表存用户基本信息;会员扩展表存储用户扩展信息,如用户的各种标签信息等。
所以数据由面向应用的操作型环境进入数据仓库后,需要进行数据集成。将面向应用的数据转换为面向主题数据仓库数据,本身就是一种集成。具体体现在如下几个方面:
- 命名规范的统一。表名、字段名等统一;
- 字段类型的统一。相同和相似字段的字段类型的统一;
- 公共代码及代码值的统一。公共代码及标志性字段的数据类型、命名方式等的统一;
- 业务含义相同的表的统一。主要依据高内聚、低耦合的理念,在物理实现中,将业务关系大、源系统影响差异小的进行整合;业务关系小、源系统影响差异大的进行分而置之。通常有如下集成方式:
a、采用主从表的设计方式,两表或多表都有的字段放在主表中(主要基本信息),从属信息分别放在各自的从表中。对于主表中的主键,要么采用复合主键,源主键和系统或表区别标志;要么采用唯一主键,“源主键||系统或表区别标志”生成新的主键。通常建议采用复合主键的方式。
b、直接合并,共有信息和个性信息都放在同一个表中。如果表字段的重合度较低,会出现大量空值,对于存储和易用性会有影响,需谨慎选择。
c、不合并,源表的表结构及主键等差异很大,无法合并,使用数据仓库里的多个表存放各自数据。
维度表的整合涉及的内容和上面介绍的几个方面相同,下面重点看一下表级别的整合,有两种表现形式。
第一种是垂直整合,即不同的来源表包含同一数据集,只是存储的信息不同。比如淘宝会员在源系统有多张表,会员基础信息表、会员扩展信息表、淘宝会员等级信息表、天猫会员等级信息表。这些表都属于会员相关信息表,依据维度设计方法,尽量整合至会员维度模型中,丰富其维度属性。
第二种是水平整合,即不同的来源表包含不同的数据集,不同子集之间无交叉,亦可以存在部分交叉,比如针对蚂蚁金服数据仓库,其采集的会员数据有淘宝会员、1688 会员、国际站会员、支付宝会员等,是否需要将所有的会员整合成一张会员表?如果进行整合,首先需要考虑各个会员体系是否有交叉,如果存在交叉,则需要去重;如果不存在交叉,不同子集的自然键是否存在冲突,如果不冲突,可以考虑将各子集的自然键作为整合后的表的自然键;另外一种方式是设置超自然键,来源+各子集的自然键加工成一个字段作为超自然键。在阿里巴
巴,常用的方式是将来源各子集的自然键作为联合主键的方式,并且在物理实现时,将来源字段作为分区字段。
有整合就有拆分,到底是做整合还是拆分,都有哪些因素决定。我们在下面两节讨论维度的水平拆分和垂直拆分。
4.2 维表拆分
当一张维度表中包含多个类别、加工逻辑十分困难、有部分维度属性可以单独处理或者不常用时,考虑将维度拆分。无论是维表是分还是合,都需要从以下角度权衡:当业务变化时,模型是否容易扩展;是否易用;是否有查询效能问题。
在设计过程中需要重点考虑以下三点原则:
- 扩展性:指在源系统、业务逻辑变化的时候,能通过少的成本快速扩展模型,保持核心模型的相对稳定性。软件工程中高内聚、低耦合的思想是重要的指导方针之一。
- 效能:性能和成本方面的平衡。通过牺牲一定的存储成本,达到性能和逻辑上的优化。
- 易用性:模型可理解性高、访问复杂度低。用户能够方便的从模型中找到对应的数据表,并能够方便查询和分析。
4.2.1 水平拆分
维度通常可以按照类别或类型细分。比如淘系商品表,根据业务线或行业等可以对商品进行细分,比如淘宝的商品、天猫的商品、1688 的商品、飞猪的商品、淘宝海外的商品、天猫国际的商品等。不同分类的商品,其维度属性可能相同,也可能不同。比如飞猪商品和普通的淘系商品,都属于商品,都有商品价格、标题、类型、上架时间、类目等维度属性,但是飞猪的商品除了有这些公共属性,还有酒店、景点、门票、旅行等自己独特的维度属性。
4.2.2 垂直拆分
另一方面的问题是某些维度属性的来源表产出时间较早,而某些属性的来源表产出时间较晚;或者某些维度属性的热度高、使用频繁,而某些维度属性热度低、较少使用;或者某些维度属性经常变化,而某些属性比较稳定。
4.3 历史归档
前台有一套数据归档的策略,比如将商品状态为下架或删除的且最近 31 天未更新的商品归档至历史库;具体逻辑根据不同业务有不同的算法,且有特殊的规则。
数据仓库中,理所当然可以借用前台数据库的归档策略,定期将历史数据归档至历史维表。在实践中,设计商品维度表和历史商品维度表,每天将历史数据归档至历史商品维度表。关于归档策略的选择,可以有以下几种方式:
- 同前台归档策略,将前台归档算法在数据仓库中实现,定期将历史数据进行归档。但存在一些问题,一方面是前台归档策略复杂,实现成本较高;另外一方面,前台归档策略可能会经常变化,会导致数据仓库归档算法也要随之变化,维护和沟通成本较高。此方法适用于同前台归档策略逻辑较为简单,且变更不频繁的情况
- 同前台归档策略,但采用数据库变更日志的方式。对于如此庞大的数据,采用的数据抽取策略一般是通过数据库 binlog 解析获取每日增量,通过增量merge 全量的方式获取最新全量数据。可以使用增量日志的删除标记,作为前台数据归档的标记。通过此标记对数据仓库的数据进行归档。此方法不需要关注前台归档策略,简单易行。但对前台应用的要求是数据库的物理删除只有在归档时才执行,应用中的删除只是逻辑删除。
- 数据仓库自定义归档策略。可以将归档算法用简单直接的方式实现,但此方法的原则是尽量比前台应用晚归档、比前台应用少归档。避免数据仓库中已经归档的数据再次更新的情况出现。(一般用这种,比如归档30天前的日志文件)
如果技术条件允许,能够解析数据库 binlog 日志,建议使用归档策略 2,规避前台算法。具体可以根据自身数据仓库的实际情况进行选择。
5. 处理缓慢变化维度属性
(☆☆☆重点掌握)
数据仓库中另一重要的特点是——反映历史变化。缓慢变化维度,这里的缓慢是跟(快速变化)事实表相对的。我们举 2 个例子来看看:
Case1:客户的性别变更。可能在第一次登陆中,我们得到的信息是该客户性别是男。但在几年的客户再一次使用中,我们又得到该客户性别是女。这就是维度值的一种变化可能,一般并不会改变,所以大概率是其中的一次数据有误。但也有可能是客户做了变性手术。
Case2:雇员的部门更替。假定有一个雇员叫小杨,他最早是负责运营的——此时他的 title 是"商品运营助理";但因为某些原因,他转组成为数据组的一员,这时 title 就变成了"数据分析专员"。这是缓慢变化维的一种常见可能。
上面提到的这些数据变化,业务系统(CRM、OA 等)往往并不会保留历史数据。但在分析角度,我们是一定要保留这些改变的痕迹。这种随着时间可能会缓慢变化的维度,就是 缓慢变化维、也就是 SCD(Slowly Changing Dimensions)。
那么怎么去处理这种缓慢变化的维度呢?缓慢变化维一般采用以下几种解决方法:
5.1 原样保留或者重写
原样保留或者重写,这种方式理论上都是取最新的值作为维度的最终的取值,每个维度保留一条数据。保留原样这种处理方式是最简单的,直接将原系统的维度同步过来使用就可以,不用做过多的处理。重写就是指,与业务数据保持一致,直接 update 为最新的数据。
这种方法主要应用于以下两种情况:
- 数据必须正确——例如用户的身份证号,如需要更新则说明之前录入错误。
- 无需考虑历史变化的维度——例如用户的头像 url,这种数据往往并没有分析的价值。因此不做保留。
这种处理方式的优点在于:简化 ETL,直接 update 即可;节省存储空间—其他存储方法都占用更多空间。
5.2 增加新的维度行(拉链表)
插人新的维度行,每当维度发生变化的时候,插入新增的一行。采用此种方式,保留历史数据,维度值变化前的事实和过去的维度值关联,维度值变化后的事实和当前的维度值关联。也就是一个维度会存在多行的数据,按时间范围将维度与事实表关联。
我们这里再回顾 2 个概念:自然键即指有业务意义的唯一 ID,例如用户 ID、身份证号等。代理键则可以简单理解为该表的自增 ID 值。
在这种场景下,具体的 ETL 过程如下:
(1)自然键第一次出现时,新增一行数据,created 为业务系统的创建时间,updated 为 9999-12-31(数仓的规范不允许数据存在 NULL 值的情况,因此用9999-12-31 代替)。这时候存储的数据是这样的:
(2)当维度发生变化时,将自然键当前记录的 updated 由 9999-12-31 刷为最新时间,新增一行记录,记录最新的数据,created 为最新时间,updated 为9999-12-31。
这样一来,因为事实表存储的是维度表的代理键而非自然键,因此在历史数据的查询中会以历史的维度值进行计算。同时在维度值更新后的相关数据自然使用的是新的代理键。完美的解决了大部分缓慢变化维情况。
5.3 增加新属性
当需要分析所有伴随着新值或旧值的变化前后记录的事实时,上述两种方法都不能解决问题。这时我们可以添加维度列,采用这种方式,主要是为了将变化前后记录的事实归为变化前的维度或者归为变化后的维度。
假定一家公司的销售是按照销售区域进行分组:
突然有一天,领导灵机一动,决定 精细化销售,将东部、南部、北部重新划分为东南部、东北部。
但由于发送的过于仓促,因此销售人员是立刻使用了新的部门划分;但同样希望保留旧的名称——至少要暂时保留,用以比较今年和去年的业绩。
此时引入第 3 种处理方法:新增字段同时储存新旧值。
如果发生第二次变化,当前的 current 会被更新到 previous 中,新的变化值则会写入 current。当然我们也可以看到,新增维度列,这种只合适变化频率非常非常低的维度属性(毕竟频繁变化我们不可能会一直新增列来保存,特殊情况除外)。
5.4 快照存储
快照存储,这种方式就是每一个周期定时保存一份数据,与第二点有点像,不过这里会产生很多冗余的数据,当维度里大部分行在周期内,变动频繁的时候,可以采用。综合考虑维护成本和满足业务需求,个人建议采用,具体要根据业务实际情况来选择。
5.5 历史拉链存储
什么是历史拉链表?历史拉链表是维护了历史状态,以及最新状态数据的一种表。拉链表存储的数据实际上相当于快照,只不过做了优化,去除了一部分不变的记录而已,通过拉链表可以很方便的还原出拉链时点的客户记录。拉链表既能满足反应数据的历史状态,又可以最大程度的节省存储,提高查询效率。
历史拉链存储是基于处理缓慢变化维的第 2 种方法来加工的,也就是:新建维度行。但不同的是,拉链存储还特地用了两个时间键(生效时间和失效时间)来替代原有的代理键。本质其实就是为了节省存储,其次才是为了反映历史变化。如果一天内变化多次,那就以当天最后一次变更记录为最新。
每一位去医院就诊的用户,都会在医院的 HIS 系统中,录入用户信息,以用户信息表为例:
在 2024-01-01,有以下用户就诊:
在 2024-01-02,U01 用户更新了地址(由 01 更新为 A01),U04 是新增用户:
在 2024-01-03,U04 更新了地址(由 04 更新为 A04),U05,U06 是新增用户
根据以上数据,我们设计出拉链表数据如下:
数据说明:
begin_date 表示该条记录的生命周期开始时间,
end_date 表示该条记录的生命周期结束时间;
end_date = ‘9999-12-31’表示该条记录目前处于有效状态;
如果查询当前所有有效的记录,则只需要卡一下结束时间
select * from patient_info_his where end_date = ‘9999-12-31’
同样,如果我们想查在 2024-01-02 的所有有效记录:
select * from patient_info_his where ‘2024-01-02’>=begin_date and ‘2024-01-02’<=end_date。
查出来的结果就是 2024-01-02 日的两条医疗记录。
所以最后会发现,如果每条数据每日都变化,天拉链会没有意义。反之,每天所有数据都不发生变化,此时,只需要存储一天的数据即可,也不需要拉链。
--ODS 层用户增量表,用于存放每日用户新增数据
create table if not exists ods.patient_info_delta(
uid string comment '用户ID',
address string comment '用户地址',
create_date string comment '常见日期'
) partitioned by (dd string)
stored as aliorc tableroperties ('comment'='用户信息增量表');
-- 拉链表
create table if not exists ods..patient_info_his(
uid string comment '用户 ID',
address string comment '用户地址',
create_date string comment '创建日期',
start_date string comment '生命周期开始时间',
end_date string comment '生命周期结束时间'
)
stored as aliorc tableroperties ('comment'='用户信息拉链表');
-- 实现的 sql 代码
-- 以 2024-01-01 为第一天,并且已经初始化好,现在更新 2024-01-02 数据,实现的 sql 如下:
insert overwrite table ods.patient_info_his
select
*
from (
select
t1.uid,
t1.address,
t1.create_date,
t1.start_date,
(case
when t1.end_date = '9999-12-31' and t2.uid is not null then '2024-01-01'
else t1.end_date
end) as end_date
from ods.patient_info_his t1
left join ods.patient_info_delta t2
on t1.uid = t2.uid
union
select
t3.uid,
t3.address,
t3.create_date,
'2021-01-02' as t_start_date,
'9999-12-31' AS t_end_date
from ds.patient_info_delta t3
) as t4
问题 1:拉链表的优化:
- 设置一个分区字段 is_history,存储历史数据和当前生效数据,更新的时候用当前生效数据分区数据;
问题 2:拉链表如果某一天数据是错误的该如何处理?
- 一定保留最近 1 年的明细数据,方便数据回滚到错误数据的前一天,然后重跑相关任务和下游数据。
6. 特殊维度
6.1 递归层次
维度的递归层次,按照层级是否固定分为均衡层次结构和非均衡层次结构。比如类目,有固定数量的级别,分别是叶子类目、五级类目、四级类目、三级类目、二级类目、一级类目;比如地区,分别是乡镇/街道、区县、城市、省份、国家。对于这种具有固定数量级别的递归层次,我们称为均衡层次结构。
6.1.1 层次结构扁平化
降低递归层次使用复杂度的最简单和有效的方法是层次结构的扁平化,通过建立维度的固定数量级别的属性来实现,可以在一定程度上解决上卷和下钻的问题。对于均衡层次结构,采用扁平化最为有效。
具体数据存储示例如下,其中四级和五级类目省略:
但存在如下三个方面的问题:
- 针对某类目上卷或下钻之前,必须知道其所属的类目层级,然后才能决定限制哪一级类目,如上述示例,限制一级类目 ID 等于 21。
- 假设分三级类目统计最近一天交易 GMV,由于某些叶子类目直接是一级类目或二级类目。比如类目 ID 等于 121456022 的类目,其是叶子类目。和交易事实表关联之后,由于其对应的三级类目为空,导致根据三级类目统计最近一天交易 GMV 时,类目 ID 等于 121456022 的交易无法被统计到。下游数据统计时,为了规避此问题,如此类目对应的三级类目为空,则取二级类目,如二级类目仍为空,则取一级类目(回填)。
- 第三个问题就是扁平化仅包含固定数量的级别,对于非平衡层次结构,可以通过预留级别的方式解决,但扩展性会较差。
6.1.2 层次桥接表
上一节提到的类目,使用树型结构表示如下:
6.2 行为维度
在电商平台数据仓库中,经常存在很多如下的维表。比如卖家的主营类目、卖家主营品牌、用户常用地址等。其中卖家主营类目和主营品牌使用、卖家的商品分布情况和卖家的交易分布情况,通过算法计算得到卖家的主营类目和主营品牌;其中卖家常用地址,使用最近一段时间内物流中卖家的发货地址和买家的收货地址进行统计得到。类似维度,都和事实相关,如交易、物流等,称之为行为维度,或事实衍生的维度。
对于行为维度,有两种处理方式。一种是将其冗余至现有维度表中,如将卖家信用等级冗余至卖家维度表中。另外一种方式是加工单独的行为维度表,如卖家主营类目。具体采用哪种方式主要参考如下两个原则:
第一,避免导致维度的过快增长。比如我们对商品表进行了极限存储,如果将商品热度加入现有商品维度表,可能会导致每日商品变更占比过高,而导致极限存储效果较差。
第二,避免耦合度过高。比如卖家的主营类目,加工逻辑异常复杂,如果融合进现有的卖家维度表,过多业务耦合会导致卖家维度表刷新逻辑复杂、维护性差、产出延迟等。
6.3 多值维度
对于多值维度,其中一种情况是,事实表的一条记录在某维度表中有多条记录对应。
针对多值维度,常见的处理方式有三种,可以根据业务的表现形式和业务的统计分析需求进行选择。
第一种处理方式是降低事实表的粒度。在交易中,前台业务和商业智能关注于交易子订单,所以在数据仓库模型设计中,将交易订单设计为子订单粒度,对于每个子订单,都只有一个商品相对应。对于其中的事实,则采用分摊到子订单的方式解决。但假如事实表的粒度是不能降低的,多值维度的出现是无法避免的。
第二种处理方式是多字段的方式。比如房地产销售中,每次合同签订可能存在多名买受人的情况,如夫妻合买等。对于合同签订事实表,每条记录可能对应多个买受人,而合同已经是此事实中的最细粒度,无法通过降低粒度的方式解决。由于合同签订的买受人一般不会太多,所以多字段的方式一般即可解决,考虑到扩展性,可以通过预留字段的方式解决,如超过三个买受方时,其余买受方填写至“其他买受方”字段。模型设计如下:
第三种处理方式是较为通用的桥接表方式。桥接表方式更加灵活、扩展性更好,但逻辑复杂、开发和维护成本较高,可能带来双重计算的风险,选择此方案需慎重。通过在事实表和维度表之间开发一张分组表,通过此分组表建立连接。模型设计如下:
6.4 多值属性
维度表中的某个属性字段同时有多个值,此种情况称为多值属性,也是多值维度的另一种表现形式。
对于多值属性,有三种常见的处理方式,和多值维度的第一种表现形式(上一节)相比既有相同点也有不同点,可以根据具体情况进行选择。
第一种方式是保持维度主键不变,将多值属性放在维度的一个属性字段中。比如,对于商品属性(注:此属性是业务上含义,和维度建模中的维度属性含义不同),可以通过 KV 对的形式放在 property 字段中,数据示例如下:10281239:156426871; 137396765:29229; 137400766:3226633。此种处理方式扩展性好,但数据使用较为麻烦。
第二种处理方式也是保持维度主键不变,但将多值属性放在维度的多个属性字段中。比如卖家的主营类目,由于卖家店铺中可能同时会销售男装、女装、内衣等,所以卖家的主营类目可能有多个,但业务需求只取根据算法计算得到的TOP3。针对此种情况,维度的多值属性字段具体值的数量固定,可以采用多个属性字段进行存储,方便数据的统计分析和报表展示。如果多值属性字段具体值的数量不固定,也可以采用预留字段的方式,但扩展性较差。卖家的主营类目维度设计如下:
第三种处理方式是维度主键变化,一个维度值存放多条记录。比如商品 SKU维表,对于每个商品,有多少 SKU,即有多少记录,主键是商品的 ID 和 SKU 的ID。此种处理方式扩展性好,使用方便,但需要考虑数据的急剧膨胀。比如淘宝商品属性表采用了此种处理方式,数据记录达到几百亿的级别,对于查询性能是个不小的挑战。