01
数仓建模综述
数据建模是数据开发工作中的核心与基石,好的模型体系好处很多:
- 降低成本:优秀的模型设计能够提升数据复用性,减少计算/存储资源浪费
- 提升开发效率:优秀的模型设计能够降低数据使用门槛,减少工作量
- 提升质量:优秀的模型设计能够保证数据口径一致,降低bug率
数据建模的实现方式有很多,常用的比如ER模型,Data Vault模型等。目前业界使用最多的模型是Ralph Kimball 在《数据仓库工具》中提出的维度建模模型,其中典型的代表如星型模型,雪花模型。一个典型的维度建模一般需要经过如下几个步骤:
- 业务调研:调研需要建模的业务形态,划分基本的业务线/数据域
- 层次设计:定义数仓层级,保证各层级之间职责明确,划分清晰
- 规范设计:定义数仓中表/字段的命名规范,建立统一的指标体系
- 事实表设计:根据单一/复合业务过程确定事实表主题,确定最小粒度
- 维度表设计:根据业务确定实体,补充实体属性字段
优秀的层次设计可以保证数仓表数量在可控范围内增长,同时保证数据产出流逻辑清晰,便于后期维护和扩展。良好的规范设计规定了统一的命名规则,保证各个业务过程的实体/指标的完备和唯一性。
02
设计原则
按照《大数据之路——阿里巴巴大数据实战》,维度建模应该符合以下几个规范
- 高内聚,低耦合:从业务流程和数据访问特性两个角度考虑,针对业务粒度相近,业务流程相近的数据应该放在同一个表中(例如广告数仓中通常会把广告的点击/曝光/转化多个业务过程数据放在同一个宽表中),针对经常要在同一个场景下访问的数据,也应该放在同一个表内。
- 公共处理逻辑下沉和单一:公用的逻辑应该封装在底层表中,避免公用逻辑直接暴露给上层,同一个公共逻辑需要收敛,避免在多个地方同时存在
- 适当冗余:考虑到mr/rdd计算框架下join运算的资源损耗,可以通过适当冗余字段处理减少join操作
- 命名一致/可理解:同一个业务含义的字段命名必须相同,且直观可读。
03
层次设计
腾讯视频数据中心团队把数据模型分成了五层:操作数据层(ODS),主题明细层(DWD),主题聚合层(DWS),应用数据层(ADS),维度数据层(DIM)
- 操作数据层(ODS):最接近数据源的一层,主要负责以下工作:
数据清洗:过滤日志或者上游中的脏数据 结构化:将数据中的json等非结构化字段结构化处理 保留原始数据:根据业务或者系统要求保留历史切片,方便溯源
- 主题明细层(DWD)/主题聚合层(DWS): 采用维度退化的方法将维度指标退化到事实表中,减少下游维度表和事实表的关联,同时在DWS层采用更多的宽表化操作构建公共指标,提高指标复用性,主题层通常是数仓中的核心数据层,主要工作如下:
复用逻辑:维度退化:宽表化处理,复用关联逻辑
统一公共指标/公共维度:保证公共指标,公共维度的数据一致性,减少因数据来源不一致带来的数据问题
- 应用数据层(ADS):面向分析和产品的数据层,灵活多变,数据会存储到多种查询引擎,常见的如HIVE,mysql,es等中便与业务使用,降低查询门槛,提高应用中的查询响应速度
- 维度数据层(DIM):贯穿数据模型各个层次,保留业务过程中的实体信息,用来关联事实表将数据宽表化
其他团队也有各自的分层方式,例如字节跳动的ODS(操作数据层),DWD(主题明细层),DW(主题聚合层),DM(宽表层),ADS(应用层)。阿里巴巴数据团队把数据模型分为 ODS(操作数据层),DWD(主题明细层),DWS(主题聚合层),ADS(应用层)
04
规范设计
为了保证数仓中同一个业务含义字段命名一致,需要通过某种方式约束/规定字段的命名,以阿里巴巴的命名规范为例。在阿里的数据体系中的每个指标都可以通过如下的规则描述。
- 业务板块:业务形态差距较大的多个业务可以通过业务板块来区分,例如广告业务和电商业务属于两个业务板块
- 时间周期:时间粒度,常见的如每天,每月,每个季度
- 数据域:多个业务过程的抽象,例如广告效果相关业务可以整合成一个数据域(点击/曝光/展示),电商行业的交易业务可以整合成一个数据域(下单/成交/退款)
- 修饰类型:一系列修饰词的集合,比如终端类型
- 修饰词:除了维度以外的对指标的限定,比如移动端的广告点击,这里的移动端就是修饰词
- 业务过程:业务中的一个原子的业务行为,特点是不可拆分,比如点击广告这个业务行为
- 原子指标:业务过程下某一个行为的度量,特点是不可拆分,比如广告点击量,每个原子指标有确定的数据类型,算法说明以及命名
- 维度:维度是用来描述业务过程中的环境(参与方)集合,比如广告点击这个业务行为中的维度有用户,广告订单,广告位,广告所处的页面等
- 属性:属性是用来描述维度的内容,比如用户的名称,用户的性别,用户的年龄等。
- 派生指标:通过一个派生指标描述业务过程,派生指标 =1个原子指标 n个修饰词(n>=0,也可以没有,全部通过维度来描述) 时间周期,例如移动端最近3天的广告点击量 = 点击量 (原子指标) 移动端(修饰词) 3天(时间周期) = yidong_r3d_click
从上图可以看出,派生指标由原子指标,修饰词和时间周期组合得到,并且遵循以下几个原则:
- 原子指标,修饰词归属到某一个数据域下
- 派生指标只能完全归属到同一个原子指标下面,相应的也会继承 对应原子指标的数据域,数据类型,算法说明
- 派生指标可以由多个修饰词共同修饰,不同修饰词之间的逻辑关系 是/或/否
ps:对于每一个原子指标,修饰词以及时间周期都需要设定一个词根,常见的命名方式通常为 英文缩写/英文全称/中文缩写,这些词根应该具备较强的可读性,并且在部门内外部有统一的命名方式(通过工具或者文档构建原子指标库/修饰词库)。
05
模型实施
业务调研和架构设计
在构建数仓模型之前首先要分析建模的业务过程,主要包含以下三块工作:
- 调研业务场景和数据需求,划分业务板块和数据域
- 设计数仓架构:构建总线矩阵,抽象数据域下的业务过程和维度主体,构建总线矩阵
- 根据数据需求整理指标体系,规范指标定义和命名
业务调研
业务调研主要从两方面出发:
1. 分析各个业务线/业务模块的异同点,具有相同业务形态的业务应该配置为同一个业务板块。例如字节跳动的抖音,抖音国际版(Tiktok),不难看出两者除了多语言模块以外,其他功能几乎一样。因此在数仓建模的时候应该考虑将两者维护在同一个数据仓库之下,减少重复开发。
功能模块 | 用户 | 视频 | 广告主 | 广告订单 | 跳转页 |
---|---|---|---|---|---|
广告点击 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ⭕️ |
广告转化 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ⭕️ |
广告曝光 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ❌ |
2. 分析业务的分析需求,具有交叉分析需求的业务应该配置为同一个业务板块。数仓服务于数据查询/分析,通过下沉复杂逻辑减少数据查询/分析的代价。对于数仓开发同学来说,理想的数仓开发是业务分析的产品具有完全一致的业务过程,但是产品之间形态千差万别才是常态。这种情况下应该尽量以查询需求为准,抽象共同的业务过程,将不同的业务线融合,统一维护。例如在 字节跳动的商业化部门数仓中,为了分析整个公司收入,会将各个业务线(星图,穿山甲,DOU ,即合等)的收入数据,进行统一维护。当然业务线融合也会在很大程度上提高了数仓开发难度,主要体现在 SLA 维护,维度属性字段产出/维护,指标统一性维护,在介绍事实表建设/维度表建设两章时会详细描述。
产品名称 | 业务类型 |
---|---|
今日头条 | 新闻应用 |
抖音 | 短视频应用 |
火山小视频 | 短视频应用 |
西瓜小视频 | 短视频应用 |
穿山甲 | 联盟广告y应用 |
架构设计
- 明确了模型中的业务领域之后首先需要将整个业务领域划分成多个业务过程,业务过程可以概括为不可以拆分的行为(原子性),每个行为由维度(实体) 度量(指标)组成,同一个业务步骤中具有相似维度的的业务过程可以组成一个数据域。初次开发时不需要将当前业务域中的所有业务过程都拆分出来,可以根据使用方的分析需求逐步扩展。如 广告业务中的,广告点击/曝光/转化这几个行为事件。
- 规定了数据域之后就要开始构建总线矩阵了。明确每个数据域中有哪些业务过程,每个业务过程中有哪些维度和指标。注意,在明确维度的时候需要和业务方提前沟通,将有分析需求的维度尽可能包含进来。需要注意的时有些分析的维度不直接包含在业务过程中,例如广告主不直接包含在该广告点击的业务过程中,但是为了满足业务分析需求,也应该作为维度包含进来
业务过程 | 广告受众 | 广告订单 | 广告位 | 播放视频 | 转化操作 | 广告主 |
---|---|---|---|---|---|---|
广告点击 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ❌ | ⭕️ |
广告曝光 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ❌ | ⭕️ |
广告转化 | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ⭕️ | ⭕️ |
规范定义
根据定义的总线架构获得了业务过程中的实体和业务过程,我们可以构建出命名的规范图。例如广告业务的效果域总线架构可以产出如下的规范定义图。
通过上图可以构建出一致性的维度属性和一致性的指标,例如衍生指标最近三天pc平台曝光数 = recent_3_d_pc_exposure。唯一归属在 广告业务——效果域下,其中exposure 原子指标规定了唯一的曝光的口径(例如 广告业务中的曝光为广告海报整体露出大于1/3且持续3秒)和单位(次)
06
维度表建设
维度是分析业务过程中的环境信息,维度表的列值被称为维度属性,维度属性通常被用来作为分析的过滤/排序/聚合条件。因此在分析需求中作为约束条件的字段应该构建成维度属性。
在确定了需要构建的维度实体之后通常按照以下流程设计
- 确定维度全局唯一,同一个业务下有且只允许一个维度定义
- 确定维度主键,维度的主键包括代理键和自然键两种,前者可以通过系统自动生成(例如mysql的自增id),自然键通常由业务提供,例如每个广告订单id都是唯一的,并且对应前台的业务系统来说,每一个自然键都具有唯一的业务定义
- 确定主维表与主维表属性,主维表是业务前台直接同步的数据,属性字段由业务系统维护
- 确定相关维表和相关维表属性,数仓的主要工作是对不同系统的交叉分析,因此在构建维表时除了填充业务前端的属性以外还需要根据业务的分析需求补齐相关属性字段,例如广告订单的广告主行业信息。
除此之外,在设计过程中,开发人员还需要注意以下几个主题:反规范化,维度一致性处理,维度整合和拆分
反规范化
传统的联机事务处理系统(OLTP)通常采用规范化技术来设计数据模型,一种常见的规范化模型是雪花模型,这种模型的优势最大化的减少数据冗余,同时针对更新的数据,雪花模型能够将数据更新操作控制在少数的几个表记录中,从而减少系统更新压力
但是不同于传统的分析事务,在联机分析处理系统(OLAP)中主要面临的业务场景是数据追加数据查询操作,系统中保存的是海量的快照数据,同时根据mr/spark计算引擎可知,数仓系统的性能瓶颈在集中在表JOIN。因此对于维度数据,需要采用反规范化处理,将多个维度属性尽量存放在同一张表内。减少JOIN操作,相应的数据模型被称为星型模型。
维度一致性处理
保持维度一致性是数仓分析需求的前提和基石,数仓分析中的很多需求都是将不同业务领域的业务过程或者同一业务领域下的不同业务过程合并起来分析的,如果存在维度不一致的情况(例如针对同一个广告位,维度一中的枚举是 A,维度二的枚举是B,或者广告客户的行业属性只在业务一中存在,在业务而中不存在)会导致交叉分析失败,通常来说处理维度不一致有以下几个手段:
- 共享维表,例如现在的 ams 广告系统中很大的一部分工作量就是将spa和gaiya的维度数据融合,共用一个维度表
- 维度上卷,将不同业务的维度构建层次关系,保证某一个层次的维度能够完全包含在另外一个维度中
- 抽象共同维度,针对存在不一致情况的维度,可以把一致的维度字段抽离,生成共同维表,交叉分析只放在具有共同维度的属性上面
维度整合和维度拆分
为了保证维度一致性,往往需要将不同的维度表整合成同一份数据,在整合过程中,需要保证命名规范,数据类型,业务含义统一。常用的维度整合方式有如下几种:
- 通过主从表整合维度,将多个表的公共字段抽象成一个维度主表,不同的其他信息分别放在各自的从表中,主表和从表之间可以通过复合主键(通常是源系统标志 源系统id)关联
- 直接合并,将多个表的维度全部放在一个表中,构建一个全局的维度宽表。
除此之外,应该怎么判断哪些维度表是应该整合的呢?通常从以下两个方面去判断
- 多个维度表是否代表同一个业务数据集,例如spa 和 ams 广告投放系统中的广告位类型在腾讯视频app上都是同一个业务定义,应该尽量整合到一起
- 多个维度数据是否有交叉分析的需求,例如 字节跳动的商业化部门会对多个业务产品线的收入做交叉分析,因此需要把各个业务线的广告主/消费者 维度进行整合
有整合就有拆分,维度拆分通常在以下几种场景中有需求:
- 整合的维度表中业务关联不大,或者没有分析需求,可以将这部分数据水平拆分出去
- 不同业务线的维度属性差距过大,可以将这部分数据水平拆分出去
- 某些维度属性产出时间较晚且没有高频的分析需求,可以将这部分属性字段拆分出去,保证核心维度产出SLA(常见)
07
事实表建设
数仓中使用事实表来描述业务过程,表中的每一条记录代表一个完整的业务过程记录,由描述业务过程环境的维度和描述业务动作的指标组成。按照指标的类型可以将事实表分为事务事实表,周期快照事实表和累计快照事实表
- 事务事实表用来描述的业务过程,保存了最细粒度的数据,也称为原子事实表,常见的事务表包括日志/流水数据
- 周期快照表是通过规律性的时间间隔保存事实,例如天/月/季度/年表,保存当前时间间隔内累计的业务过程指标
- 累计快照事实表事务在特定时间点的最终状态,通常表中会有保留多个特定的时间点。
事实表的设计原则
- 保证事实表与业务过程强关联:数仓的建设目的是提高分析效率,在事实表设计中应该尽可能的包含当前业务过程中的所有事实指标且只包含与当前业务相关的指标,例如广告下单这一业务过程中,不应该包含支付金额这一事实。
- 保证事实表记录的可加性:事实表中的指标应该尽量保证可加,针对不可加类指标,应该分解成可加指标,例如转化率指标应该分解出 曝光数和转化数同时存放在事实表中。其次需要保证表中的事实粒度一致,同一个事实表中不能存在不同粒度的事实
- 优化指标易用性,例如保证事实单位一致,处理Null值,退化维度字段到事实表中
如何设计一个事实表
- 选择一个业务过程:设计事实表之前首先需要明确业务流程,从而选择有分析需求的业务过程构建事实表,例如以广告订单流转过程为例,业务需要针对广告的在app中的实际执行情况分析部门广告收入,因此针对执行投放计划这一业务过程构建事实表,此时的事实表中就是包含广告执行相关的单事务事实表,只应该包含与广告执行相关的维度和指标。
- 声明事实表粒度:在确定了需要分析的业务过程之后,事实表的粒度也就确认了,需要注意的构建时应该尽量以最细粒度构建,例如广告执行过程中有广告订单维度,每个订单下有多个广告位,这时候应该以广告位作为最细粒度构建事实表
- 确定维度:在确定了事实表粒度之后,也就确认了表中的主键,除此之外应该将尽可能多的维度信息打平存放在表中,例如广告执行表除了广告位这一主键粒度以外,还会存放广告主,设备信息,频道页面信息等维度,便于后期做多维分析。
- 确认事实:事实是衡量业务过程的度量,度量应该尽可能完备,同时需要保证事实的粒度与表粒度一致,不可加/半可加事实需要分解成可加事实
- 冗余维度:根据星型模型,事实表中应该尽量将关联维度的属性字段冗余在表中,减少下游join操作
近期热文
除了票房,电影、电视剧还能怎样靠画面赚钱?
基于时间线的Feed流后台系统设计
谈谈微前端领域的js沙箱实现机制
喜欢本文?快点“在看”支持一下