维度是维度建模的基础和灵魂,将度量称为”事实“,环境描述为“维度”,用于分析事实所需要的多样环境。
数据仓库的能力直接与维度属性的质量和深度成正比,维度设计方法:
第一步:选择维度或者新建维度,在数据仓库中要保证维度的唯一性,比如淘宝商品维度,有且只允许有一个维度定义。
第二步:确定主维表,一般主维表是ODS表,直接与业务系统同步。
第三部:确定相关维表, 根据对业务的梳理,确定哪些表和主维表存在关联,并选择其中的一些用来生成维度属性
第四步:确定维度属性,包括从主维表和相关维表中选择维度属性或生成新的维度属性
确定维度属性原则:
维度中的一些描述属性以层次方式或者一对多的方式关联,可以理解为包含连续主从关系的属性层次。
在属性的层次结构中可以进行钻取,最底层代表最低级别的详细信息,最高的代表该要信息。
GMV(全称Gross Merchandise Volume),即商品交易总额。
大多数OLTP的底层数据通过规范化处理将重复属性移至其自身所属的表中,删除冗余数据。这种方法可以有效避免数据冗余导致的不一致性。
比如在OLTP中的商品表和类目表,商品表和类目表是一对多的关系,且商品表中有冗余的类目表的属性字段。如果对某类目进行更新,可能要修改很多商品表,这个不太合理。在OLAP中数据是稳定的,不存在这个问题
维度的属性层次合并到单个维度的操作称为反规范化。
为什么要反规范化?
数据仓库总线架构的重要基石之一就是 一致性维度。
在针对不同数据域进行迭代构建或者并行构建时,需要将不同数据域的业务过程或者同一数据域的不同业务 合并在一起观察。
难点:
维度不一致,会导致交叉探查出现问题,包括维度格式和内容的不同:
维度一致性的表现形式
数据仓库是一个面向主题的、集成的、 非易失的且随时间变化的的数据集合,用于对管理决策过程的支持。
集成是四个特性中最重要的。
整合的难点:
重要数据来源是大量、分散的,来自各种应用。应用很少会考虑到和其他系统的集成
应用在编码、命名习惯、度量单位等方面差异很大。如性别可以是1、0;F、M;
应用处于性能和扩展性考虑,物理实现方式不同。
所以要集成,需要做到:命名规范的统一、字段类型的统一、公共代码及代码值的统一、业务含义相同的表的统一(高内聚、低耦合)
整合的方式
垂直整合
水平整合
维度拆分的背景:
维度通常可以按照类别或者类型细分。比如淘系商品表,可以分为淘宝的商品、天猫商品、飞猪旅行商品。不同分类的商品,维度属性有相同点和不同点。比如航旅的商品和普通的商品,都有商品价格、标签等,但是航旅商品还有独特的属性维度(酒店、门票),如何设计维度呢?
维度设计的三个原则:
垂直拆分
需要考虑前面提到的三个原则,设计主从维度。主模型的稳定和产出时间的提前对数据仓库的稳定和下游应用的产出有重要意义。
主维表存放稳定、产出时间早、热度高的属性。 需要进行缓慢变化的处理
从维表存放变化较快、产出时间晚、热度低的属性。
水平拆分
需要考虑维度的不同分类的属性差异 和 业务的关联程度。
可以将维度的不同分类实例化为不同的维度,同时在主维度中保存公共属性,核心维度比较稳定
两个业务相关性比价低,耦合在一起弊大于利,如淘系商品和16888商品,属于不同的BU(业务单元),各自发展,一方修改还有一方要被迫修改。
对于庞大的数据量,前端将商品状态为下架或者删除的且最近31天未更新的商品归档至历史库。数据仓库也可借鉴定期将历史数据归档至历史维表
策略一:同前台归档策略,定期对历史数据归档。 缺点:一、归档策略复杂,实现成本高;二、前台归档策略变化快,维护沟通成本高。适用于逻辑简单且变更不频繁的情况。
策略二:同前台归档策略,但是采用数据库变更日志的方式。通过数据库binlog日志解析获取每日增量,通过增量merge全量方式获取最新的全量数据。可以用增量日志的删除标志作为归档标志,前台应用中的删除只是逻辑删除
策略三:数据仓库自定义归档策略
为什么要有缓慢变化的维度?
在数据仓库的重要特点之一是反映历史变化。现实世界中,维度的属性不是静态的,他会随着时间的流逝发生缓慢的变化(缓慢是和数据增长较快的事实表相比的)。当业务数据库中的一些数据发送了变化,如顾客的联系方式发生改变,一些基本信息的更改可能会引起数据归纳和分析出现的问题。
方式一:重新维度值,不保留历史数据。
方式二:插入新的维度行,维度值变化钱的事实和过去的维度值关联
方式三:添加维度列,是由于方式二不能将变化前后记录的事实归一为变化前或者变化后的维度。
Kimball的维度建模理论中,必须使用代理键作为每个维表的主键,用于处理缓慢变化维。但是在阿里并没有使用
为什么不使用代理键呢?
如何解决缓慢变化维呢?
采用快照的方式,每天保留一份全量快照数据,通过限定日期采用自然键进行关联就可。优点:简单而高效、使用方便好理解。缺点:存储浪费
用来解决存储浪费的问题
历史拉链存储
通过新增两个时间戳字段(start_dt和end_dt),将所有以天为粒度的变更数据都记录下来,通常分区字段也是时间戳字段。下游可以通过限制时间戳字段来获取历史数据,但是解释成本高,且存储方式用start_dt和end_dt做分区,时间推移分区数量膨胀。
极限存储(对历史拉链的优化)
一、透明化
底层还是历史拉链存储,上层做一个视图操作或者在hive里做一个hook,通过分析语句的语法树,转化查询。
select * from a where ds = 20211116
select * from a where start_dt <= 2021116 and end_dt > 20211116
二、分月做历史拉链
如果用start_dt和end_dt做分区,不做限制的话,一年最多可能产生的分区数是 365 x 364 / 2 = 66430。使用极限存储的话,可能产生的分区数:12 x (1 + (30+29)/ 2) = 5252。
|-- 202101/ # 每月一个周期
|----20210101/202101——INFINITY #每月1日的全量数据
|----20210101/20210102 #1月1日产生的1月2日死亡的数据
但是仍有缺点:
一、产出效率低,大部分极限存储通常需要t-2;
二、对于变化频率高的数据不能达到节约成本的效果。
对应的改进:
一、在极限存储前有一个全量存储表,全量存储表仅保留最近一段时间的全量分区数据,历史数据通过映射方式进行关联。
二、对于部分变化频率频繁的字段需要过滤。如用户积分字段每天在变化。不过滤的话,相当于每个分区存储一份全量数据,起不到节约存储成本的效果
将一部分不稳定的属性从主维度中移出,并将他们放到拥有自己代理键的新表中来。这些属性互相之间没有直接关联,不存在自然键。
通过为每个组合创建新行的一次性过程来加载数据,比如VIP等级有8个值,用户信用等级有18个,那么在此微型维度中有8 x 18个组合,代理键可能是 1- 144。
优点:
可以解决维度的过度增长导致极限存储效果大打折扣的问题
缺点:
一、微型维度的局限性,事先用所有可能值的组合加载的,要考虑每个属性的基数且是枚举值。
二、ETL逻辑复杂,生成代理键和使用代理键加工都很复杂
三、破坏了维度的可浏览性,可能造成无法对应统计和未维护历史信息的问题
指的是某维度的实例值的层次关系。按照层次是否固定分类均衡层次结构(地区,乡镇、曲线、城市、省份、国家)和非均衡层次结构(每个公司可能存在一个母公司,但没有固定的递归层次)
如何统计类目ID为1的最近一天GMV?
第一步:获取父类目ID为1的所有目录的所有类目,称为子类目。
第二步:每个子类目,如果为叶子类目,终止;如果不是叶子则继续往下找。
第三步:将所有叶子类目和交易事实表关联进行统计汇总。
很多的数据仓库系统不支持递归SQL,且成本较高。要对此层次结构进行处理
1.层次结构扁平化
通过建立维度的固定数量级别的属性来实现, 对于均衡层次结构,扁平化最有效。
如:类目 —— 类目名称 ——一级类目ID —— 二级类目ID —— 是否叶子类目。
每个类目保存一条记录,将其所属的各类目层级属性化。
如何统计呢?限制一级类目ID等于1之后进行汇总。
但是存在三个问题:
2.层次桥接表
不需要预先知道层级,不用回填,也能解决非均衡层次问题,灵活性好。复杂性高,使用成本高(事实表和桥接表的多对多关系带来了双重计算的隐患)。
划分为以下几种
行为维度的处理方式
原则:
事实表的一条记录在某维表中有多条记录与之对应。比如一个订单,可能包含多个商品,交易父子订单。
常见的三种处理方式:
第一种:降低事实表的粒度,比如可以设置为子订单力度(商品力度)
第二种:采用多字段,比如房地产交易,有多个买受方。对于合同签订事实表,合同已经时此事实中最细力度了,可以用多字段的方式解决,有买受方一、买受方二。
第三种:使用桥接表,需要谨慎使用,可能带来双重计算的风险。
维表中的某个属性字段同时有多个值,称为多值属性。如每个商品有一到多个SKU(Stock Keeping Unit 库存量单位)、属性(不同尺码、颜色分类)
常见的处理方式:
第一种:保持主键不变,将多值属性放在维度的一个属性字段中,用k-v对的形式保存。
第二种:保持主键不变,将多值属性放在维度的多个属性字段,比如卖家主营类目就是取的TOP3,可以固定数量。
第三种:维度主键发生变化,一个维度值存放多条记录,每个商品有多少SKU就有多少记录。数据容易膨胀
由操作系统中的指示符或者标志字段组合而成,不在一致性维度之列。
比如:交易订单的交易类型字段(话费充值、航旅);支付状态、物流状态;交易留言、交易属性(k-v对)、交易标签。