数据仓库是一个面向主题的、集成的、非易失的且随时间变化的数据集合。在数据仓库中,维表是其中一个关键的组成部分。维表设计的好坏直接影响到数据仓库中数据的可用性和可靠性。

维度设计基础

维度的基本概念

维度是维度建模的基础和灵魂。在维度建模中,将度量称为“事实”,将环境描述为“维度”,维度是用于分析事实所需要的多样环境。例如,在分析交易过程中,可以通过买家、卖家、商品和时间等维度描述交易发生的环境。

维度所包含的表示维度的列,称为维度属性。维度属性是查询约束条件、分组和报表标签生成的基本来源,是数据易用性的关键。例如,在查询请求中,获取某类目的商品、正常状态的商品等,是通过约束商品类目属性和商品状态属性来实现的;统计淘宝不同商品类目的每日成交金额,是通过商品维度的类目属性进行分组的;我们在报表中看到的类目、BC 类型(B 指天猫,C 指集市)等,都是维度属性。所以维度的作用一般是查询约束、分类汇总以及排序等

如何获取维度或维度属性

如何获取维度或维度属性?如上面所提到的,一方面,可以在报表中获取;另一方面,可以在和业务人员的交谈中发现维度或维度属性。因为它们经常出现在查询或报表请求中的“按照(by)”语句内。例如,用户要“按照”月份和产品来查看销售情况,那么用来描述其业务的自然方法应该作为维度或维度属性包括在维度模型中。

维度使用主键标识其唯一性,主键也是确保与之相连的任何事实表之间存在引用完整性的基础。主键有两种:代理键和自然键,它们都是用于标识某维度的具体值。但代理键是不具有业务含义的键,一般用于处理缓慢变化维;自然键是具有业务含义的键。比如商品,在 ETL 过程中,对于商品维度的每一行,可以生成一个唯一的代理键与之对应;商品本身的自然键可能是商品 ID 等。

维度的基本设计方法(★)

维度的设计过程就是确定维度属性的过程,如何生成维度属性,以及所生成的维度属性的优劣,决定了维度使用的方便性,成为数据仓库易用性的关键。正如 Kimball 所说的,数据仓库的能力直接与维度属性的质量和深度成正比。

下面以淘宝的商品维度为例对维度设计方法进行详细说明。

第一步:选择维度或新建维度。 作为维度建模的核心,在企业级数据仓库中必须保证维度的唯一性。以淘宝商品维度为例,有且只允许有一个维度定义。

第二步:确定主维表。 此处的主维表一般是 ODS 表,直接与业务系统同步。以淘宝商品维度为例,s_auction_auctions 是与前台商品中心系统同步的商品表,此表即是主维表。

第三步:确定相关维表。 数据仓库是业务源系统的数据整合,不同业务系统或者同一业务系统中的表之间存在关联性。根据对业务的梳理,确定哪些表和主维表存在关联关系,并选择其中的某些表用于生成维度属性。以淘宝商品维度为例,根据对业务逻辑的梳理,可以得到商品与类目、SPU、卖家、店铺等维度存在关联关系。

第四步:确定维度属性。 本步骤主要包括两个阶段,其中第一个阶段是从主维表中选择维度属性或生成新的维度属性;第二个阶段是从相关维表中选择维度属性或生成新的维度属性。以淘宝商品维度为例,从主维表 s_auction_auctions 和类目、SPU、卖家、店铺等相关维表中选择维度属性或生成新的维度属性。

确定维度属性的几点提示

  • 尽可能生成丰富的维度属性

比如淘宝商品维度有近百个维度属性,为下游的数据统计、分析、探查提供了良好的基础。

  • 尽可能多地给出包括一些富有意义的文字性描述

属性不应该是编码,而应该是真正的文字。在阿里巴巴维度建模中,一般是编码和文字同时存在,比如商品维度中的商品 ID 和商品标题、类目 ID 和类目名称等。ID 一般用于不同表之间的关联,而名称一般用于报表标签。

  • 区分数值型属性和事实

数值型字段是作为事实还是维度属性,可以参考字段的一般用途。如果通常用于查询约束条件或分组统计,则是作为维度属性;如果通常用于参与度量的计算,则是作为事实。比如商品价格,可以用于查询约束条件或统计价格区间的商品数量,此时是作为维度属性使用的;也可以用于统计某类目下商品的平均价格,此时是作为事实使用的。

  • 尽量沉淀出通用的维度属性

有些维度属性获取需要进行比较复杂的逻辑处理,有些需要通过多表关联得到,或者通过单表的不同字段混合处理得到,或者通过对单表的某个字段进行解析得到。此时,需要将尽可能多的通用的维度属性进行沉淀。一方面,可以提高下游使用的方便性,减少复杂度;另一方面,可以避免下游使用解析时由于各自逻辑不同而导致口径不一致。例如,淘宝商品的 property 字段,使用 key:value 方式存储多个商品属性。商品品牌就存储在此字段中,而商品品牌是重要的分组统计和查询约束的条件,所以需要将品牌解析出来,作为品牌属性存在。又例如,商品是否在线,即在淘宝网站是否可以查看到此商品,是重要的查询约束的条件,但是无法直接获取,需要进行加工,加工逻辑是:商品状态为 0 和 1 且商品上架时间小于或等于当前时间,则是在线商品;否则是非在线商品。所以需要封装商品是否在线的逻辑作为一个单独的属性字段。

维度的层次结构

维度中的一些描述属性以层次方式或一对多的方式相互关联,可以被理解为包含连续主从关系的属性层次。层次的最底层代表维度中描述最低级别的详细信息,最高层代表最高级别的概要信息。维度常常有多个这样的嵌入式层次结构。比如淘宝商品维度,有卖家、类目、品牌等。商品属于类目,类目属于行业,其中类目的最低级别是叶子类目,叶子类目属于二级类目,二级类目属于一级类目。

在属性的层次结构中进行钻取是数据钻取的方法之一。下面通过具体的例子,看看如何在层次结构中进行钻取。

假设已有一个淘宝交易订单,创建事实表。现在统计 2015 年“双十一”的下单 GMV(商品交易总额),得到一行记录;沿着层次向下钻取,添加行业,得到行业实例个数的记录数;继续沿着层次向下钻取,添加一级类目,得到一级类目实例个数的记录数。可以看到,通过向报表中添加连续的维度细节级别,实现在层次结构中进行钻取。

  • 最高层次的统计

image

  • 钻取至行业层次

image

  • 钻取至一级类目层次

image

规范化和反规范化

当属性层次被实例化为一系列维度,而不是单一的维度时,被称为雪花模式。大多数联机事务处理系统(OLTP)的底层数据结构在设计时采用此种规范化技术,通过规范化处理将重复属性移至其自身所属的表中,删除冗余数据。

这种方法在 OLTP 系统中可以有效避免数据冗余导致的不一致性。比如在 OLTP 系统中,存在商品表和类目表,且商品表中有冗余的类目表的属性字段,假设对某类目进行更新,则必须更新商品表和类目表,且由于商品和类目是一对多的关系,商品表可能每次需要更新几十万甚至上百万条记录,这是不合理的。而对于联机分析处理系统(OLAP)来说,数据是稳定的,不存在 OLTP 系统中所存在的问题。

对于淘宝商品维度,如果采用雪花模式进行规范化处理,将表现为下图所示的形式:

image

将维度的属性层次合并到单个维度中的操作称为反规范化。分析系统的主要目的是用于数据分析和统计,如何更方便用户进行统计分析决定了分析系统的优劣。采用雪花模式,用户在统计分析的过程中需要大量的关联操作,使用复杂度高,同时查询性能较差;而采用反规范化处理,则方便、易用且性能好。

对于淘宝商品维度,如果采用反规范化处理,将表现为下图所示的形式:

image

如上所述,从用户角度来看简化了模型,并且使数据库查询优化器的连接路径比完全规范化的模型简化许多。反规范化的维度扔包含与规范化模型同样的信息和关系;从分析角度来看,没有丢失任何信息,但复杂性降低了。

采用雪花模式,除了可以节约一部分存储外,对于 OLAP 系统来说,没有其他效用。而现阶段存储呃成本非常低。出于易用性和性能的考虑,维表一般是很不规范化的。在实际应用中,几乎总是使用维表的空间来换取简明性和查询性能。

一致性维度和交叉探查

构建企业级数据仓库不可能一蹴而就,一般采用迭代式的构建过程。而单独构建存在的问题是形成独立型数据集市,导致验证的不一致性。Kimball 的数据仓库总线架构提供了一种分解企业级数据仓库规划任务的合理方法,通过构建企业范围内一致性维度和事实来构建总线架构。

一致性维度和事实参考如下资料:数据仓库一致性维度和一致性事实

数据仓库总线架构的重要基石之一就是一致性维度。在针对不同数据域进行迭代构建或并行构建时,存在很多需求是对于不同数据域的业务过程或者同一数据域的不同业务过程合并在一起观察。对于日志数据域,统计了商品维度的最近一天的 PV(页面浏览量)和 UV(独立访问用户数);对于交易数据域,统计了商品维度的最近一天的下单 GMV(商品交易总额 )。现在将不同数据域的商品的事实合并在一起进行数据探查,如计算转化率等,称为交叉探查。

如果不同数据域的计算过程使用的维度不一致,就会导致交叉探查存在问题。当存在重复的维度,但维度属性或维度属性的值不一致时,会导致交叉探查无法进行或交叉探查结果错误。接上个例子,假设对于日志数据域,统计使用的是商品维度 1;对于交易数据域,统计使用的是商品维度 2。商品维度 1 包含维度属性 BC 类型,而商品维度 2 无此属性,则无法再 BC 类型上进行交叉探查;商品维度 1 的商品上架时间这一时间属性格式是 yyyy-MM-dd HH:mm:ss,商品维度 2 的商品上架时间这一维度事项时间格式是 UNIX timestamp,进行交叉探查时如果需要根据商品上架时间做限制,则复杂性较高;商品维度 1 不包含阿里旅行的商品,商品维度 2 包含全部的淘系商品,交叉探查也无法进行。还有很多种形式的不一致,这里不再一 一列举,但基本可以划分为维度格式和内容不一致两种类型。

上面对维度不一致性进行了详细分析,下面总结维度一致性的几种表现形式:

  • 共享维表

比如在阿里巴巴的数据仓库中,商品、卖家、买家、类目等维度有且只有一个。所以基于这些公共维度进行的交叉探查不会存在任何问题。

  • 一致性上卷

其中一个维度的维度属性是另一个维度的维度属性的子集,且两个维度的公共维度属性结构和内容相同。比如在阿里巴巴的商品体系中,有商品维度和类目维度,其中类目维度的维度属性是商品维度的维度属性的子集,且有相同的维度属性和维度属性值。这样基于类目属性进行不同业务过程的交叉探查也不会存在任何问题。

  • 交叉属性

两个属性具有部分相同的维度属性。比如在商品维度中具有类目属性,在卖家维度中具有主营类目属性,两个维度具有相同的类目属性,则可以在相同的类目属性上进行不同业务过程的交叉探查。

一致性维度(Conformed Dimension) 是指跨多个主题域(如销售、库存、订单等)共享的维度,这些维度在不同业务场景中具有统一的定义、属性和粒度。一致性维度是数据仓库建模的核心原则之一,其目标是确保不同业务数据在维度层面的统一,避免数据冗余和分析结果的不一致。

维度设计高级主题

维度整合

我们先来看数据仓库的定义:数据仓库是一个面向主题的、集成的、非易失的且随时间变化的数据集合,用来支持管理人员的决策。其中集成是数据仓库特性中最重要的一个。

数据仓库的重要数据来源是大量的、分散的面向应用的操作型环境。不同的应用在设计过程中,可以自有决策,主要满足本应用的需求,很少会考虑和其他系统进行数据集成。应用之间的差异具体表现在如下几个方面:

  • 应用在编码、命名习惯、度量单位等方面会存在很大的差异。比如不同应用对于用户的性别编码不同,有 0 和 1、F 和 M 等;不同应用的用户 ID 含义相同,但字段名称不同,有 ueruser_id 等;不同应用对于金额的度量单位不同,有元、分等
  • 应用出于性能和扩展性的考虑,或者随技术架构的演变,以及业务的发展,采用不同的物理实现。拆分至不同类型数据库中,部分数据采用关系型数据库存储(如 Oracle、MySQL 等),部分数据采用 NoSQL 数据库存储(如 HBase、Tair 等)。拆分成同一类型数据库中的多个物理表,比如对于淘宝商品,有商品主表和商品扩展表,商品主表存储商品基本信息,商品扩展表存储商品特殊信息,如不同产品线的定制化信息等;对于淘宝会员,有会员主表和会员扩展表,会员主表存储用户基本信息,会员扩展表存储用户扩展信息,如用户的各种标签信息等。

  • 所以数据由面向应用的操作型环境进入数据仓库后,需要进行数据集成。将面向应用的数据转换为面向主题的数据仓库数据,本身就是一种集成。具体体现在如下几个方面

  • 命名规范的统一。表名、字段名等统一。
  • 字段类型的统一。相同和相似字段的字段类型统一。
  • 公共代码及代码值的统一。公共代码及标志性字段的数据类型、命名方式等统一。
  • 业务含义相同的表的统一。主要依据高聚合、低耦合的理念,在物理实现中,将业务关系大、源系统影响差异小的表进行整合;将业务关系小、源系统影响差异大的表分而置之。通常有如下几种集成方式:
  • 采用主从表的设计方式,将两个表或多个表都有的字段放在主表中(主要基本信息),从属信息分别放在各自的从表中。对于主表中的主键,要么采用复合主键、源主键和系统或表区别标志;要么采用唯一主键。通常建议采用复合主键的方式
  • 直接合并,共有信息和个性信息都放在同一个表中。如果表字段的重合度较低,则会出现大量空值,对于存储和易用性会有影响,需谨慎选择
  • 不合并,因为源表的表结构及主键等差异较大,无法合并,使用数据仓库里的多个表存放各自的数据。
  • 维度的整合涉及的内容和上面介绍的几个方面相同,下面重点看表级别的整合,有两种表现形式。

    垂直整合

    第一种是垂直整合,即不同的来源表包含相同的数据集,只是存储的信息不同。比如淘宝会员在源系统中有很多个表,如会员基础信息表、会员扩展信息表、淘宝会员等级信息表、天猫会员等级信息表,这些表都属于会员相关信息表,依据维度设计方法,尽量整合至会员维度模型中,丰富其维度属性。

    水平整合

    第二种是水平整合,即不同来源包含不同的数据集,不同子集之间无交叉,也可以存在部分交叉。比如针对蚂蚁金服的数据仓库,其采集的会员数据有淘宝会员、1688会员、国际站会员、支付宝会员等,是否需要将所有的会员整合到一个会员表中呢?如果进行整合,首先需要考虑各个会员体系是否有交叉,如果存在交叉,则需要去重;如果不存在交叉,则需要考虑不同子集的自然键是否存在冲突,如果不冲突,则可以考虑将各子集的自然键(具有业务含义)作为整合后的表的自然键;另一种方式是设置超自然键,将来源表各子集的自然键加工成一个字段作为超自然键。在阿里巴巴,通常采用将来源表各子集的自然键作为联合主键的方式,并且在物理实现时将来源作为分区字段。

    维度拆分

    有整合就有拆分,到底是整合还是拆分,由多种因素决定。

    水平拆分

    维度通常可以按照类别或类型进行细分。比如淘系(含淘宝、天猫和聚划算)商品表,根据业务线或行业等可以对商品进行细分,如淘宝的商品、天猫的商品、1688的商品、飞猪旅行的商品、淘宝海外的商品、天猫国际的商品等。不同分类的商品,其维度属性可能相同,也可能不同。比如航旅的商品和普通的淘系商品,都属于商品,都有商品价格、标题、类型、上架时间、类目等维度属性,但是航旅的商品除了有这些公共属性外,还有酒店、景点、门票、旅行等自己独特的维度信息。

    如何设计维度?针对此问题,主要有两种解决方案:方案 1 是将维度的不同分类实例化为不同的维度,同时在主维表中保存公共属性;方案 2 是维护单一维度,包含所有可能的属性。

    选择哪种方案?在数据模型设计中需要考虑的因素有很多,基本不可能满足各个特性指标的最优化。在设计过程中需要重点考虑以下三个原则:

    • 扩展性:当源系统、业务逻辑变化时,能通过较少的成本快速扩展模型,保持核心模型的相对稳定性。软件工程中的高内聚、低耦合的思想是重要的指导方针之一。
    • 效能:在性能和成本方面取得平衡。通过牺牲一定的存储成本,达到性能和逻辑的优化。
    • 易用性:模型可理解性高、访问复杂度低。用户能够方便地从模型中找到对应的数据表,并能够方便地查询和分析。

    垂直拆分

    在维度合计内容中,我们提到维度是维度建模的基础和灵魂,维度属性的丰富程度直接决定了数据仓库的能力。在进行维度设计时,依据维度设计的原则,尽可能丰富维度属性,同时进行反规范化处理。对于具体实现时可能存在的问题,一是在“水平拆分”中提到的,由于维度分类的不同而存在特殊的维度属性,可以通过水平拆分的方式解决此问题。

    二是某些维度属性的来源表产出时间较早,而某些维度属性的来源表产出时间较晚;或者某些维度属性的热度高、使用频繁,而某些维度属性的热度低、较少使用;或者某些维度属性经常变化,而某些属性比较稳定。在“水平拆分”中提到的模型设计的三个原则同样适合解决此问题。 出于扩展性、产出时间、易用性等方面考虑,设计主从维度。主维表存放稳定、产出时间早、热度高的属性;从维表存放变化较快、产出时间晚、热度低的属性。比如在阿里巴巴数据仓库中,设计了商品主维度和商品扩展维度。其中商品主维度在每日的 1:30 左右产出,而商品扩展维度由于有冗余的产出时间较晚的商品品牌和标签信息,在每日的 3:00 左右产出。另外,由于商品扩展维度有冗余的库存等变化较快的数据,对于主维度进行缓慢变化的处理较为重要。通过存储的冗余和计算成本的增加,实现了商品主模型的稳定和产出时间的提前,对于整个数据仓库的稳定和下游应用的产出都有较大意义。

    历史归档

    阿里巴巴历史截至当前的淘系(含淘宝、天猫和聚划算)商品有几百亿条记录,在 MaxCompute 中,一天的全量数据占用约 36TB 的存储。面对如此庞大的数据量,如何设计模型、如何降低存储、如何让下游方便获取数据,成为必须要解决的问题。对于历史数据,是否存在前台已经不再使用的情况?答案是肯定的,对于如此庞大的数据量,现有的技术架构也很难处理。前台有一套数据归档策略,比如将商品状态为下架或删除的且最近 31 天未更新的商品归档至历史库;具体逻辑根据不同 BU 有不同的算法,且有特殊的规则。

    在数据仓库中,可以借用前台数据库的归档策略,定期将历史数据归档至历史维表。在实践中,阿里巴巴数据仓库设计了商品维表和历史商品维表,每天将历史数据归档至历史商品维表。

    维度变化

    缓慢变化维

    数据仓库的重要特点之一是反映历史变化,所以如何处理维度的变化是维度设计的重要工作之一。缓慢变化维的提出是因为在现实世界中,维度的属性并不是静态的,它会随着时间的流逝发生缓慢的变化,与数据增长较为快速的事实表相比,维度变化相对缓慢。

    缓慢变化维相关问题,参考如下资料:数仓拉链表原理、设计以及在 ODPS 中的实现

    快照维表

    在“如何获取维度或维度属性”中,介绍了自然键和代理键的含义,在 Kimball 的维度建模中,必须使用代理键作为每个维表的主键,用于处理缓慢变化维。

    但在阿里巴巴数据仓库建设的实践过程中,虽然使用的是 Kimball 的维度建模理论,但实际并未使用代理键。那么为什么不使用代理键?如何处理缓慢变化维?

    首先看“为什么不使用代理键”这个问题。第一个原因是,阿里巴巴数据量庞大,使用的是阿里巴巴自助知识产权的分布式计算平台 MaxCompute。对于分布式计算系统,不存在事务的概念,对于每个表的记录生成稳定的全局唯一的代理键难度很大,此处稳定指某条记录每次生成的代理键都相同。第二个原因是,使用代理键会大大增加 ETL 的复杂性,对 ETL 任务的开发和维护成本很高。

    接下来讨论不使用代理键如何处理缓慢变化维的问题。在阿里巴巴数据仓库实践中,处理缓慢变化维的方法是采用快照方式。数据仓库的计算周期一般是每天一次,基于此周期,处理维度变化的方式就是每天保留一份全量快照数据。比如商品维度,每天保留一份全量商品快照数据。任意一天的事实均可以获取到当天的商品信息,也可以获取到最新的商品信息,通过限定日期,采用自然键进行关联即可。此方法既有有点,也有弊端。

    优点主要有以下两点:

    • 简单而高效,开发和维护成本低
    • 使用方便,理解性好。数据使用方只需要限定日期,即可获取到当天的快照数据。任意一天的事实快照和维度快照通过维度的自然键进行关联即可。

    弊端主要体现在存储的极大浪费上。比如某维度,每天的变化量占总体数据量的比例很低,在极端情况下,每天无变化,使得存储浪费很严重。此方法主要就是实现了牺牲存储获取 ETL 效率的优化和逻辑上的简化。但是一定要杜绝过度使用这种方法,而且必须要有对应的数据生命周期制度,清除无用的历史数据。

    特殊维度

    递归层次

    递归等次指的是某维度的实例值的层次关系,比如淘宝类目体系,如下图所示。

    image

    维度的递归层次按照层级是否固定分为均衡层次结构和非均衡层次结构。比如类目,有固定数量的级别,分别是叶子类目、五级类目、四级类目、三级类目、二级类目、一级类目;地区,分别是乡镇/街道、区县、城市、省份、国家。对于这种具有固定数量级别的递归层次,称为“均衡层次结构”。比如公司之间的关系,每个公司可能存在一个母公司,但可能没有固定的一级、二级等层次关系。对于这种数量级别不固定的递归层次,称为“非均衡层次结构”。

    淘宝交易事实表通过叶子类目和类目维表关联,如何统计类目 ID 为 21 的最近一天的 GMV?第一步,获取父类目 ID 等于 21 的所有类目,称为子类目。第二步,对于每个子类目,如果为叶子类目,则终止;如果非叶子类目,则此类目 ID 作为父类目 ID 执行第一步,直到找到所有叶子类目,如圣诞服饰(50026579)、台历(121456022)等。将所有叶子类目和交易事实表关联进行统计汇总,即可得到类目 ID 等于 21 的最近一天的 GMV,也就是家具日用类目的最近一天的 GMV。在物理实现时,可以使用递归 SQL 实现,如 Oracle 中的 connect by 语句。

    通过数据探查得知 ID 等于 21 的类目属于一级类目(父类目 ID 等于 0),统计其最近一天的 GMV 的过程,称为上钻;在递归层次中进行上钻和下钻是很常见的。由于很多数据仓库系统和商业智能工具不支持递归 SQL,且用户使用递归 SQL 的成本较高,所以在维度模型中,需要对此层次结构进行处理。

    层次结构扁平化

    降低递归层次使用复杂度的最简单的有效的方法是层次结构的扁平化,通过建立维度的固定数量级别的属性来实现,可以在一定程度上解决上钻和下钻的问题。对于均衡层次结构,采用扁平化最有效。

    对于淘宝商品类目,通过层次结果扁平化之后,类目维表如下图所示。

    image

    每个类目保存一条记录,并将其所属的各类目层级属性化。其中,对于高层级类目,由于其无低层级类目,则低层级类目置为空值。

    具体数据存储示例如下表所示,其中四级和五级类目省略。

    image

    如何统计类目 ID 为 21 的最近一天的 GMV?将淘宝交易事实表通过叶子类目和类目维表的类目 ID 关联之后,限制一级类目 ID 等于 21 之后进行汇总统计,即可以得到类目 ID 等于 21 的最近一天的 GMV。其使用方便性得到大大提高,但存在如下三个方面的问题:

    • 针对某类目上钻或下钻之前,必须知道其所属的类目层级,然后才能决定限制哪一级类目。如上述所示,限制一级类目 ID 等于 21
    • 假设分三级类目统计最近一天的 GMV,由于某些叶子类目直接是一级类目或二级类目(比如类目 ID 等于 121456022 的类目,其是二级类目且为叶子类目),和交易事实表关联之后,其对应的三级类目为空,导致根据三级类目统计最近一天的 GMV 时,类目 ID 等于 121456022 的交易无法被统计到。下游数据统计时,为了规避此问题,如果此类目对应的三级类目为空,则取二级类目;如果二级类目仍未空,则取一级类目。 所以针对此问题,下游数据统计时,类目层次结构扁平化的另一种方式是回填,将类目向下虚拟。具体数据存储示例如下图所示,其中红色字体部分为回填内容。 image
    • 扁平化仅包含固定数量的级别,对于非平衡层次结结构,可以通过预留级别的方式来解决,但扩展性较差。

    多值维度

    对于多值维度,一种情况是事实表的一条记录在某维度表中有多条记录与之对应。比如对于淘宝交易订单,买家一次购买了多种商品,如一件毛衣和两双袜子,称为交易父订单;对于每种商品的交易,称为交易子订单;此交易父订单有两个子订单与之对应。假设设计交易父订单事实表,则对于此事实表的每一条记录,在商品表中都有一到多条记录与之对应。

    针对多值维度,常见的处理方式有三种,可以根据业务的表现形式和统计分析需求进行选择。

    • 第一种处理方式是降低事实表的粒度。在淘宝交易中,前台业务和商业智能关注交易子订单,所以在数据仓库模型设计中,将交易订单设计为子订单粒度,对于每个子订单,只有一种商品与之对应。对于其中的事实,则采用分摊到子订单的方式来解决。但很多时候,事实表的粒度是不能降低的,多值维度的出现是无法避免的。
    • 第二种处理方式是采用多字段。比如在房地产销售中,每次合同签订都可能存在多个买受方的情况,如夫妻合买等。对于合同签订事实表,每条记录可能对应多个买受方,而合同已经是事实中的最细粒度,无法通过降低粒度的方式来解决。由于合同签订的买受人一般不会太多,所以一般采用多字段方式。考虑到扩展性,可以通过预留字段的方式,如超过三个买受方时,其余买受方填至“其他买受方”字段。模型设计如下图所示。
    • 第三种处理

    多值属性

    维表中的某个属性字段同时有多个值,称之为“多值属性”。它是多值维度的另一种表现形式。在阿里巴巴的数据仓库中,存在很多维表,如商品 SKU 维表、商品属性维表、商品标签维表等。每个商品均有一到多个 SKU、一到多个属性和一到多个标签,所以商品和 SKU、属性、标签都是多对多的关系。又如,一个人可能有多个电话号码,一个学生可能选修了多门课程。这些都是多值属性的示例。通常,多值属性在关系型数据库中的表现形式为以逗号分隔的值列表,或者通过附加表来管理。

    对于多值属性,常见的处理方式有三种,可以根据具体情况进行选择。

    • 第一种处理方式是保持维度主键不变,将多值属性放在维度的一个属性字段中。比如对于商品属性,可以通过 k-v 对的形式放在 property 字段中,数据示例如下:10281239:156426871;13739676:29229;137400766:322663。此种处理方式扩展性好,但数据使用较为麻烦。
    • 第二种处理方式也是保持维度主键不变,但将多值属性放在维度的多个属性字段中。比如卖家主营类目,由于卖家主营类目可能有多个,但业务需求是只取根据算法计算得到的 TOP3。针对此种情况,维度的多值属性字段具体值的数量固定,可以采用多个属性字段进行存储,方便数据统计分析和报表展示。如果多值属性字段具体值的数量不固定,则可以采用预留字段的方式,但扩展性较差。卖家主营类目维度设计如下图所示。 image
    • 第三种方式是维度主键发生变化,一个维度值存放多条记录。比如商品 SKU 维表,对于每个商品,有多少 SKU,就有多少记录,主键是商品的 ID 和 SKU 的 ID。此种处理方式的扩展性好,使用方便,但需要考虑数据的急剧膨胀情况。比如淘宝商品属性表采用了此种处理方式,数据记录达到几百亿的级别。

    参考资料

    大数据之路-阿里巴巴大数据实践

    原创文章,转载请注明出处:http://www.opcoder.cn/article/95/