1 维度表的定义
在维度建模中,通常将指标的度量称之为“事实”,将产生度量的环境称之为“维度”。将描述同一个业务实体的的多个维度列组合在一起,就是常说的“维度表”。维度表是用户分析数据的窗口,其提供了事件发生过程中的环境描述信息,能够做数据查询的过滤条件和数据分析的分组。维度设计既是维度建模的基础,也是其关键所在,可以说,维度表设计的质量,将决定了整个维度模型分析能力的上限。
2 维度表的构成
维度表通常表示的是参与到业务过程中的一个业务实体(如,商品、卖家、买家、优惠券、时间等等),其每一行数据表示的是一个业务实体的实例(如,商品维度表中的一行表示的就是一个商品)。
2.1 维度表的结构
维度表一般由【代理键、自然键、维度属性】三部分构成。如果有保留维度变化历史或大型维度表的拆分需求,可能还会加入其它部分以做技术实现上的补充。代理键:不具有任何业务含义,仅做维度表数据唯一性区分的属性,通常以主键形式出现。
自然键:具有业务含义,是业务实体一个实例的唯一性区分(如,商品ID),在维度表中不一定做表的主键。
维度属性:描述同一业务实体各种特征的维度列(如,商品维度表中的商品名称、商品价格等)。
维度表中的代理键,会根据分析的需求决定是否存在,如果没有需要保留追溯业务实体历史变化的需求,一般情况会用自然键直接做为维度表的主键,这样通常简单、高校、且易于理解。代理键的存在主要是应对处理变化维度的需求。
2.2 维度表的内容
3 设计方法
维度表的设计就是确定维度属性的过程,如何识别维度属性、如何生成维度属性,将是设计过程中需要着重考虑的地方。
3.1 维度表设计流程
3.1.1 选择实体
维度表设计首先要选择实体,也就是维度表所要描述的抽象对象。如,互联网电商在交易过程中涉及到的实体有:买家、卖家、订单、广告等等,当然还有一些在不同业务场景下衍生出来的一些业务抽象实体,如优惠券、活动、商圈等都可以作为维度实体。 实体的选择主要是结合业务流程,在需要建模的业务流程环节涉及到了哪些参与者,这些不同的参与者便是维度表描述的实体对象,维度表中的属性,就是用来区分不同实体的特性。下面用电商平台的注册环节举例说明,电商网站的注册,自然需要买家进行注册账号和个人信息,其中涉及买家(即消费者)实体。
3.1.2 确定主维表
确定主维表,主要是识别出维度表的主要数据来源。通常,业务系统中也会将相同类型业务实体进行统一存储(即一张表),亦或是在大型企业有建设业务中台会提前做同类业务实体的数据融合(如,商品中心、用户中心等)。但在没有类似业务中台可以直接获取全量维度实体数据的情况下,就需要自行确定业务实体数据的来源,并做融合。一般情况会将常规主要业务流程中产生的业务系统数据做为主维度表,因为其一般是维度表的主要数据来源,并且数据准确、丰富。例如,电商APP可能会有多个注册入口,APP、微信、支付宝、分享外链等等,我们可能会将APP注册流程中产生的数据表作为主维表,当然可能有的企业已经在用户注册系统已经将其他各种渠道注册的用户数据已经囊括进入。
3.1.3 确定辅维表
辅维表存在的目的有两方面。一方面是补全主维表在维度实体的数据(如,用户大部分是通过独立注册登录APP,还有小部分是通过微信登录、支付宝登录等等);另一方面是为了寻找维度表所表示的业务实体的一些其他属性描述辅助表(如,商品类目表、商品属性表、用户家庭关系表等),这些辅维表用来丰富维度表的属性描述,增强维度表的表现性,同样也能扩展维度表的分析能力。
3.1.4 识别维度属性
维度表的维度属性一般可以分为相对稳定的“固化属性”和变动频繁“动态属性“。
“固化属性”通常是和维度实体关系相对紧密且较为稳定的固有属性,不依赖或很少依赖于其它的业务活动过程(如,商品维度表中的商品名称、商品品牌等); ”动态属性“通常是只能基于特定的事务过程或者活动关联,在维度实体的层次上汇总得到的一些统计属性(如,描述商品的近30天销量,首次上架日期等)。
由于“固化属性”和“动态属性”的变更周期差异巨大,一般会在维度表的构建过程中结合具体的场景进行拆分,一方面是保证维度表能够高效的产出,另一方面也是为追溯历史数据提供合理的技术实现。
3.2 生成维度属性
维度属性的生成,既可能来自原始业务系统的即有的业务实体表(如,业务系统会有商品表/商品扩展表),同时也可能来自事务过程之后的数据统计(如,近商品近一天的销量),无论维度属性来自哪里,都可以参照以下建议不断地丰富和完善维度属性,维度属性越是详尽,维度模型/数据仓库的分析宽度和深度才能更强大。
3.2.1 多增加文字描述
业务操作系统同步进入数仓的数据表中,通常会将某一属性的可枚举值一代码的形式表示(如,商品类型<01:服装;02:食物>),而代码的具体含义会在表属性的说明中或者在数据字典的说明文档中。这样做的目的是方便对其维护并提高在业务系统中的存取效率。 在数据仓库这种分析型的系统中,为了能够完善其可解释性,最佳的方案是增加一个与代码表示列对应的文字描述属性列,目的是描述代码表示列的具体含义,使分析人员可以更便捷的根据需要进行过滤、访问和组织数据。
3.2.2 统一标志值
一些布尔类的属性列,其属性值在业务系统中可能会以各种不同的形式出现,如,可以是整形的0和1,或者是字符型的“Y”和“N”,亦或是“True”和“False”,统一张维度表中,不同的布尔类属性列使用不同的表示方式,将会使维度表的使用效率大打折扣,分析人员在使用维度表的布尔类属性列时需要不断的去查询说明文档,确认每个属性列的表示方式。 在生成维度属性时,将这些布尔类的属性列的标志值做统一规定,使用0/1或者Y/N进行表示,这样其表示也更加明确,使用更加便捷,也不会在生成报表过程中其意义含糊不清。
3.2.3 属性拆分
数据仓库获取的数据充满了不确定性,既会有业务系统中表属性的各种随性组合,也会有一些标准化可按照特定规则拆分的属性(如,客户的身份证号码、买家的收货地址邮编等),如果能够按照既定的规则对维度表的属性做合理的拆分,将会进一步丰富维度表的表达能力。 业务系统中存储的客户身份号码,可以依据身份号码的构成规则进行拆分。18位身份证号码,1-2位表示省、直辖市;3-4位表示地级市;5-6位表示区、县;7-14位表示出生年月;第17位表示性别(男为单,女为双)。这样,依据身份证号码的构成,一个身份证号码,可以扩展出客户户籍省、市、区县、出生年月、性别至少5个维度属性,并且出生年月还可以进一步拆分扩展,每个拆分出的属性,可参照上述1,增加对应的文字描述,完善代码属性值和文字描述属性一一对应,拆分扩展后的维度表将会有更强的表达能力。
3.2.4 属性组合
属性组合与属性拆分相互对应,属性拆分的目的是从一个固定的编码中获取更加详细的维度属性,丰富维度的可解释行;属性组合则是相反的过程,是将一些具有相关性的维度属性列进行组合,方便在查询时的维度属性使用或展示。 比如业务操作系统存储的地区代码或者地区名称,我们会根据业务上的使用习惯会进行适当的组合,将省份、城市组合,或者将城市、区县组合,在做查询应用或页面展示时都是不错的选择。通常会选择根据业务需求进行适当组合,不必穷尽所有排列组合,因为数据仓库建设的目的主要还是在于能更便捷的应用数据。
3.2.5 数值型维度属性
考虑到“事实”通常是数值型,作为“维度”的数值型属性往往容易被忽略掉。同时数值型属性究竟是该被归入“事实”中还是该被归入到“维度”中也让很多人感到困惑。简单来讲,数值型的属性既可以作为“事实”,同时也可以作为“维度”属性。 当数值型属性用作聚合汇总时,那么无疑就是“事实”;用作查询过滤条件或者分组键时,也可以作为“维度”属性。比如“商品价格”,其可以同时出现在事实表和维度表中,在事实表中可以作为指标度量计算商品成交的平均价格,在维度表中则可以通过价格去分析商品在不同价格或者不同价格区间的成交情况。
3.2.6 统计型维度属性
统计型维度属性一般指并非维度实体固有的属性,而是随着业务的发展,在业务过程中产生的一些实体相关的行为统计信息(如,近30天商品销量)。这些统计信息一般是作为分析的结果数据进行输出,但由于其描述仍然是维度实体,自然也可以作为维度属性来应用。统计型维度属性需要考虑的问题主要在于更新的频率,一般将其与维度的基本属性进行拆分开来存储,基本属性相对变化较少,而统计型维度属性易变的体质通常会作为维度基础属性表的扩展表进行存储和应用。
3.2.7 通用型维度属性
通用型维度属性是在使用数仓过程中不断总结,不断沉淀的一些与业务强相关的维度属性。一般是具有复杂计算逻辑的维度属性,如需要经过多张表关联计算,或者在企业范围内超高频使用的分析逻辑。不断的沉淀这些通用的维度属性,一方面是提高下游使用的便捷性,另一方面也能保证数据口径的一致性。如,在业务上可能需要分析分销OR自营商品数据表现,最佳实现是将商品“是否分销”的维度属性进行封装,统一构造进入维度表中,保证数据口径的一致性。
4 设计原则
数据仓库模型在设计过程中需要一些设计原则来做一些规范约束,这样才不会在建设过程中,发现越建设越杂乱,经常需要不断返工,重新推倒以前的东西,再重新纠正,导致历史包袱过重。
4.1 主键选择
维度表主键通常会有自然键、代理键两种选择:代理键:不具有任何业务含义,仅做维度表数据唯一性区分的属性;
自然键:具有业务含义,是业务实体一个实例的唯一性区分(如,商品ID,一个商品ID就代表一个独立商品)。
两种主键选择因业务而异,笔者从业依赖,所见最多的还是以“自然键”作为主键居多。一方面主要是“自然键”做主键易处理、好理解,和业务系统意义相同;另一方面大多业务场景很少有需要追溯历史变化的需求。 在此做一些建议:如业务上对该维度表表示的业务实体没有或很少有追溯历史变化的需求,可直接采用自然键作为主键使用(如,商品ID,用户ID等),因为生成代理键的过程也会有耗费大量的数据加工性能,并且如何能保证代理键的唯一性,也需要去做设计方案。
如却有业务场景肯能会需要追溯历史变化,可根据需求,结合维度表拆分,将拆分后的维度表,一部分用“自然键”做主键,另一部分涉及到缓慢变化的维度属性,通过“代理键”做主键,这样可以综合两种主键的优势,同时满足业务需求。
4.2 一致性原则
维度表的一致性是关系到数据模型能否进行跨业务横向钻取做多业务流程分析的关键。一致性原则主要体现在三个方面:结构一致性、语意一致性、内容一致性。 结构一致性:同一实体的同一维度属性在不同维度表/事实表中,需有相同的维度属性列名、相同的数据类型定义,以保证内容的同一性。 语意一致性:不同维度表/事实表中,相同维度属性所表达的业务含义需要是一致的,否则在使用过程中会出现相同指标、不同结果的数据指标不一致性。 内容一致性:是需要在同一实体同一维度属性在不同维度表中需要有相同的数据内容表示(如,下单日期维度和支付日志维度中month属性一个是‘2020-02’表示,一个‘03’表示)。
保证一致性的两个方法:共享维度表结构:同一实体的不同角色维度表共享一张维度表,通过在核心维度表上创建视图或进行数据导出实现维度表结构的共享(如:下单日期维度、支付日志维度)。
共享维度表内容:其他表加工过程中使用到维度属性内容,直接从维度表中获取,该实体的所有属性,均以维度表中属性为准,仅在维度表中进行维护,其他事实表/维度表中使用到维度表的指定属性,仅做内容共享。
4.3 空值处理原则
一个原则:避免空值。 从各业务系统获取的数据特点各异,同样在表现在对空值的表示上,“NULL”、“null”、“Null”、“N/A”等等。在构建维度表过程中,需要对此类值做明确的处理,因为进入数据仓库中经加工的数据是要作为数据资产被应用在各种业务上,不允许出现意义不明确的数据表示,否则这些数据将毫无意义,并且空值在进行数据统计时有时会出现意外的一些统计结果。 维度属性的数据值,可以通过以下三类区分:有效值、无效值、未知值。用统一明确的标识进行表示(如,“无效值”->“invalid”,“未知值”->“unknown”)。
4.4 命名规范
命名没有统一的规范,只需要在企业内部有统一规范即可,这样方便做元数据管理,同样也能增加开发/业务人员对数据的熟悉程度,能够“望文生义”,简单通过表名就能区分数据表。 可以参考以下命名规范: dw.dim_user_info_didw:表示存储表的物理库名称;
dim(dimension):表示数据仓库层次划分的维度表层(DIM);
user_info:表示维度表所表示的业务实体描述;
di:表示数据表的更新周期,(day increment)日增量更新。
4.5 生命周期管理规范
维度表的存储周期,一般是越长越好,因为我们不确定什么时候会用到多久的历史数据,况且数据仓库本身就是需要存储历史数据的。 但在实践的过程中也同样发现了一些问题,不少企业在维度表处理上,因其相比事实表数据要小很多,故通常都是将其从业务库每日进行全量同步,这样会造成大量的数据冗余。类似情况是必须要对维度表数据最生命周期处理的,否则带来的成本将远超其可能解决的业务问题。 而也有一些情况(如,维度表增量更新)在维度表生命周期的处理上要从容很多,增量处理维度表更新,或者通过缓慢变化维处理维度表变化,是又可能将维度表历史所有数据存储下来的。当然数据的时效性还是要根据业务的需求进行取舍,调整维度表合适的生命周期,及时清理/归档陈旧的历史数据。