在维度建模中,将度量成为‘事实‘,将环境描述为’维度。‘维度属性是查询约束条件、分组和报表标签生成的基本来源,是数据易用性的关键。所以维度的作用一般是查询约束、分类汇总以及排序等。
获取维度?
1:在报表中获取
2:与业务人员交谈中发现维度属性。
因为它们经常出现在查询或报表请求中的按照 by语句内。
维度使用主键标识其唯一性,主键也是确保与之相连的任何事实表之间存在引用完整性的基础。
数据仓库的能力直接与维度属性的质量和深度成正比。
在企业级数据仓库中必须保证维度有且只允许有一个维度定义。
此处的主维表一般是ODS表,直接与业务系统同步。
根据对业务的梳理,确定哪些表和主维表存在关联关系,并选择其中的某些表用于生成维度属性。
(1) 尽可能生成丰富的维度属性。
(2) 尽可能多地给出包括二些富有意义的文字性描述。
(3) 区分数值型属性和事实。
(4) 尽量沉淀出通用的维度属性,避免各自逻辑不同而导致口径不一致。
以交易订单,创建事实表。得到下单GMV举例。
最高层次的统计:
--------------------------------------------------------------------
账期 | 行 业 | 一级类目 | 下单GMV
--------------------------------------------------------------------
20221111 | ALL | ALL | 999999
--------------------------------------------------------------------
钻取至行业层次:
--------------------------------------------------------------------
账期 | 行 业 | 一级类目 | 下单GMV
--------------------------------------------------------------------
20221111 | 行业A | ALL | 1111111
--------------------------------------------------------------------
20221111 | 行业B | ALL | 2222222
--------------------------------------------------------------------
...
钻取至一级类目层次:
--------------------------------------------------------------------
账期 | 行 业 | 一级类目 | 下单GMV
--------------------------------------------------------------------
20221111 | 行业A | CATA1 | 11111
--------------------------------------------------------------------
20221111 | 行业A | CATA666 | 66666
--------------------------------------------------------------------
...
--------------------------------------------------------------------
20221111 | 行业A | CATAX | 12345
--------------------------------------------------------------------
20221111 | 行业B | CATB1 | 22222
--------------------------------------------------------------------
当属性层次被实例化为一系列维度,而不是单一维度的时候,被称为雪花模式。这种方法用在OLTP系统中可以有效避免数据冗余导致的不一致性。
在OLTP系统中:采用雪花模式进行规范化处理。避免数据冗余,并避免更新上级层次,而更新相关的一对多所有下级层次。表现如下图:
在OLAP系统中:采用雪花模式,除了可以节约一部分存储外,对于OLAP系统来说没有其他效用。而现阶段存储的成本非常低。出于易用性和性能的考虑,维表一般是很不规范化的。在实际应用中,几乎总是使用维表的空间来换取简明性和查询性能。表现如下图:
数据仓库总线架构的重要基石之一就是一致性维度。
在针对不同数据域进行迭代构建或并行构建时,存在很多需求是对于不同数据域的业务过程或者同一数据域的不同业务过程合并在一起观察。比如对于日志数据域,统计了商品维度的最近一天的Pv和UV;对于交易数据域,统计了商品维度的最近一天的下单GMV。现在将不同数据域的商品的事实合并在一起进行数据探查,如计算转化率等,称为交叉探查。
如果不同数据域的计算过程使用的维度不一致,就会导致交叉探查存在问题。
总结维度一致性的几种表现形式:
共享维表:共享维表维度有且只有一个。
一致性上卷:其中一个维度的维度属性是另一个维度的维度属性的子集,且两个维度的公共维度属性结构和内容相同。
交叉属性:两个维度具有相同的类目属性,则可以在相同的类目属性上进行不同业务过程的交叉探查。
数据仓库是一个面向主题的、集成的、非易失的且随时间变化的数据集合,用来支持管理人员的决策。
数据仓库的重要数据来源是大量的、分散的面向应用的操作型环境。
应用之间的差异具体表现在如下几个方面:
(1)应用在编码、命名习惯、度量单位等方面会存在很大的差异。比如不同应用对于用户的性别编码不同,有0和1、F和M等;
(2)应用出于性能和扩展性的考虑,或者随技术架构的演变,以及业务的发展,采用不同的物理实现。拆分至不同类型数据库中,部分数据采用关系型数据库存储(如Oracle、MYSQL等) ,部分数据采用NoSQL数据库存储(如HBase、Tair等) 。
(3)命名规范的统一。表名、字段名等统一 。
(4)字段类型的统一。相同和相似字段的字段类型统一。
(5)公共代码及代码值的统一。公共代码及标志性字段的数据类型、命名方式等统一。
(6)业务含义相同的表的统一。
》直接合并,共有信息和个性信息都放在同一个表中。如果表字段的重合度较低,则会出现大量空值,对于存储和易用性会有影响,需谨慎选择。
》不合并,因为源表的表结构及主键等差异很大,无法合并,使用数据仓库里的多个表存放各自的数据。
下面重点看表级别的整合,有两种表现形式。
第一种是垂直整合,即不同的来源表包含相同的数据集,只是存储的信息不同。
第二种是水平整合,即不同的来源表包含不同的数据集,不同子集之间无交叉,也可以存在部分交又。
出于扩展性、产出时间、易用性等方面的考虑,设计主从维度。
主维表存放稳定、产出时间早、热度高的属性;
从维表存放变化较快、产出时间晚、热度低的属性。
方案1是将维度的不同分类实例化为不同的维度,同时在主维度中保存公共属性;
方案2是维护单一维度,包含所有可能的属性。
当维度属性随类型变化较大时,将所有可能的属性建立在一个表中是不切合实际的,也没有必要这样做,此时建议采用方案1。
两个相关性较低的业务,耦合在一起弊大于利,对模型的稳定性和易用性影响较大。
在实践中,阿里巴巴数据仓库设计了商品维表和历史商品维表,每天将历史数据归档至历史商品维表。
归档策略1:同前台归档策略,在数据仓库中实现前台归档算法,定期对历史数据进行归档。
归档策略2:同前台归档策略,但采用数据库变更日志的方式。
如果技术条件允许,能够解析数据库binlog日志,建议使用归档策略2,规避前台归档算法。
现实世界中,维度的属性并不是静态的,它会随着时间的流逝发生缓慢的变化。
在一些情况下,保留历史数据没有什么分析价值;而在另一些情况下,保留历史数据将会起到至关重要的作用。
第一种处理方式:重写维度值。
采用此种方式,不保留历史数据,始终取最新数据。
变化前
---------------------------------------------------------------------------------
商品Key | 商品ID | 商品标题 | 所属类目 | 其他维度属性
---------------------------------------------------------------------------------
1000 | item 1 | titilel | 类目1 | ...
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
订单Key | 日期Key | 商品Key | 交易金额 | 其他事实
---------------------------------------------------------------------------------
9000 | 2022-11-11 | 1000 | 103.00 | ...
---------------------------------------------------------------------------------
变化后
---------------------------------------------------------------------------------
商品Key | 商品ID | 商品标题 | 所属类目 | 其他维度属性
---------------------------------------------------------------------------------
1000 | item 1 | titilel | 类目2 | ...
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
订单Key | 日期Key | 商品Key | 交易金额 | 其他事实
---------------------------------------------------------------------------------
9000 | 2022-11-11 | 1000 | 103.00 | ...
---------------------------------------------------------------------------------
第二种处理方式:插人新的维度行。
采用此种方式,保留历史数据,维度值变化前的事实和过去的维度值关联,维度值变化后的事实和当前的维度值关联。
---------------------------------------------------------------------------------
商品Key | 商品ID | 商品标题 | 所属类目 | 其他维度属性
---------------------------------------------------------------------------------
1000 | item 1 | titilel | 类目1 | ...
---------------------------------------------------------------------------------
1001 | item 1 | titilel | 类目2 | ...
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
订单Key | 日期Key | 商品Key | 交易金额 | 其他事实
---------------------------------------------------------------------------------
9000 | 2022-11-11 | 1000 | 103.00 | ...
---------------------------------------------------------------------------------
9000 | 2022-11-12 | 1001 | 93.00 | ...
---------------------------------------------------------------------------------
第三种处理方式:添加维度列。
保留历史数据,可以使用任何一个属性列。
---------------------------------------------------------------------------------
商品Key | 商品ID | 商品标题 | 属新类目 | 属旧类目 | 其他维度属性
---------------------------------------------------------------------------------
1000 | item 1 | titilel | 类目1 | 类目2 |...
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
订单Key | 日期Key | 商品Key | 交易金额 | 其他事实
---------------------------------------------------------------------------------
9000 | 2022-11-11 | 1000 | 103.00 | ...
---------------------------------------------------------------------------------
9000 | 2022-11-12 | 1000 | 93.00 | ...
---------------------------------------------------------------------------------
数据仓库的计算周期一般是每天一次,基于此周期,处理维度变化的方式就是每天保留一份全量快照数据。比如商品维度,每天保留一份全量商品快照数据。
此方法既有优点,也有弊端。
优点:
简单而有效,开发和维护成本低。
使用方便,理解性好。数据使用方只需要限定日期,即可获取到当天的快照数据。
缺点:
弊端主要体现在存储的极大浪费上。在极端情况下,每天无变化,使得存储浪费很严重。
杜绝过度使用这种方法。
历史拉链存储。通过新增两个时间戳字段 (start-dt和end-dt),将所有以天为粒度的变更数据都记录下来。
数据存储记录1
---------------------------------------------------------------------------------
商品 | dt | 卖家 | 状态 | 其他字段 |
---------------------------------------------------------------------------------
A | 20220101 | A | 上架 | ...
---------------------------------------------------------------------------------
B | 20220101 | A | 上架 | ...
---------------------------------------------------------------------------------
A | 20220102 | A | 下架 | ...
---------------------------------------------------------------------------------
B | 20220102 | A | 上架 | ...
---------------------------------------------------------------------------------
C | 20220102 | A | 上架 | ...
---------------------------------------------------------------------------------
数据存储记录2
---------------------------------------------------------------------------------
商品 | start_dt | end_dt | 卖家 | 状态 | 其他字段 |
---------------------------------------------------------------------------------
A | 20220101 | 20220102 | A | 上架 | ...
---------------------------------------------------------------------------------
B | 20220101 | 99991231 | A | 上架 | ...
---------------------------------------------------------------------------------
A | 20220102 | 99991231 | A | 下架 | ...
---------------------------------------------------------------------------------
C | 20220102 | 99991231 | A | 上架 | ...
---------------------------------------------------------------------------------
在实际生产中,做极限存储需要进行一些额外的处理。
(1)在做极限存储前有一个全量存储表,全量存储表仅保留最近一段时间的全量分区数据,用户只访问全量存储表。
(2)对于部分变化频率频繁的字段需要过滤。每天都在发生变化的字段不过滤相当于存储全量数据。
设计商品维度时,将值变化频繁的属性加入到商品维度中,极限情况是每天所有商品数据都发生变化,此时,极限存储没有意义;一种解决方法就是上一节提到的垂直拆分,保持主维度的稳定性;另一种解决方式是采用微型维度。
阿里巴巴数据仓库实践中,并未使用此技术,主要有以下几点原因:
微型维度的局限性。必须是枚举值。
ETL逻辑复杂。
破坏了维度的可浏览性。
用户使用递归SQL的成本较高,所以在维度模型中,需要对此层次结构进行处理。
降低递归层次使用复杂度的最简单和有效的方法是层次结构的扁平化,通过建立维度的固定数量级别的属性来实现。
阿里巴巴中文站的类目体系使用方式。粗体部分为回填内容。
扁平化仅包含固定数量的级别,对于非平衡层次结构,可以通过预留级别的方式来解决,但扩展性较差。
加工逻辑复杂,使用逻辑复杂,在的扁平化设计完全可以满足需求,而不需要引入复杂的桥接表。
事实衍生的维度。
对于行为维度,有两种处理方式,其中一种是将其冗余至现有的维表中,如将卖家信用等级冗余至卖家维表中;另一种是加工成单独的行为维表,如卖家主营类目。具体采用哪种方式主要参考如下两个原则:
第一,避免维度过快增长。比如对商品表进行了极限存储,如果将商品热度加人现有的商品维表中,则可能会使每日商品变更占比过高,从而导致极限存储效果较差。
第二,避免耦合度过高。比如卖家主营类目,加工逻辑异常复杂,如果融合进现有的卖家维表中,那么过多的业务耦合会导致卖家维表刷新逻辑复杂、维护性差、产出延迟等。
事实表的一条记录在某维表中有多条记录与之对应。
针对多值维度,常见的处理方式有三种,可以根据业务的表现形式和统计分析需求进行选择。
第一种处理方式是降低事实表的粒度。但很多时候,事实表的粒度是不能降低的,多值维度的出现是无法避免的。
第二种处理方式是采用多字段。考虑到扩展性,可以通过预留字段的方式,如超过三个买受方时,其余买受方填写至“其他买受方"字段。
第三种处理方式是采用较为通用的桥接表。桥接表方式更加灵活、扩展性更好,但逻辑复杂、开发和维护成本较高,
维表中的某个属性字段同时有多个值,称之为“多值属性"。
对于多值属性,常见的处理方式有三种。
1.可以通过k-v对的形式,用字符隔开放在一个字段中,此种处理方式扩展性好,但数据使用较为麻烦。
2.第二种处理方式也是保持维度主键不变,但将多值属性放在维度的多个属性字段中。如果多值属性字段具体值的数量不固定,则可以采用预留字段的方式,但扩展性较差。
3.一个维度值存放多条记录。但需要考虑数据的急剧膨胀情况。比如淘宝商品属性表采用了此种处理方式,数据记录达到几百亿的级别。
一个事实表中可能会存在多个类似的字段,如果作为事实存放在事实表中,则会导致事实表占用空间过大;如果单独建立维表,外键关联到事实表,则会出现维度过多的情况;如果将这些字段删除,则会有人不同意。
通常的解决方案就是建立杂项维度,将这些字段建立到一个维表中,在事实表中只需保存一个外键即可。会使用实体的主键作为杂项维度的主键。只考虑杂项维度,忽略其他维度