这是一本数据仓库、维度建模领域的经典著作,但是也让我觉得枯燥至极。我好久没看到这么枯燥的书了——尤其是这蹩脚的翻译,为阅读增添了不少难度。这本书枯燥的原因(除了垃圾的翻译之外),可能是它太像一本工具书了,前十章都在用一个“尽量简单”的建模例子,引申出建模所要使用的一些技术。然而一般来说这种例子都非常枯燥,不知所云。其次是作者基本没有介绍书中出现的术语,比如“无事实的事实表”,“自然键”等,因此看到这些术语即觉得拗口,也很难快速吸收文字里的知识。如今已经读完这本书一月有余,希望用脑子里还剩下的东西来写一篇博文,说说这本书带给了我什么知识,必要的时候辅以工作中的实际例子来讲解一些术语。这篇文章先会进行背景介绍和基础术语介绍,然后讲解书中提到的基本建模方法。
数据仓库中的表分为事实表和维度表。事实表一般存储了一系列事件,通常带有一些用以分析的度量(比如金额等数字),而维度表一般存储着一些相互关联的属性。举个最简单的例子,一个订单表是事实表,每一行存储着一次购物行为。而一个订单通常和以下这些“维度”都有关系,比如用户信息,比如物流信息,比如时间信息。
我们知道大数据领域,一般不太遵循SQL那一套规范化,也就是允许行与行之间有很多重复的字段。那为什么数仓里不直接把各种维度直接展开,全放到事实表里,而是要费劲地整一套维度建模理论?
首先,这里讨论的数仓并不只是数据的仓库,而是通常有一些实时查询的需求,下游可以直接从数仓的数据来构建BI报表。也就是整本书讨论的是可以支持BI下游的数仓,而不是一些基于HDFS之类的数仓。有了这一条件,数仓就有性能要求,因此抽取维度表的好处就有:
通过上面的一大段阐述,就是为了说明维度建模是很重要、很实用的技术。只有技术有其实用性,接下来才有必要讨论如何更好地进行建模。
经典的BI产品有微软的PowerBI等。BI产品可以方便地搭建动态的可视化报表。比如我们都用Excel画过柱状图、饼图等,这些就是可视化的图形。把这些图形放在一个页面里,可以叫它一个报表。比方说某个商店的报表里可以包含:
这样,管理者可以一目了然他/她最关心的一些指标,方便他做出决策。
那什么叫动态报表呢?从我使用BI的经验来看,动态主要体现在两个方面
介绍过事实表,和数仓在BI侧的应用,接下来可以讲解事实表的粒度设计。粒度在本书中被通篇强调,因为粒度决定了下游可以进行分析的精细程度。
比方说我们有一个原始订单表,记录了用户的每一个订单,那么订单表可以有两种设计方式,这两种设计方式的粒度不同:
这两种设计的最大区别是什么?从下游应用(下游应用包含BI场景,或者其它分析场景)来看,如果下游希望进行商品维度的分析,那么只有第2种方式能满足。那你可能会说,我把每个订单购买的商品信息存下来不就行了吗?这样会增加分析的复杂程度,毕竟订单和商品是一对多的关系,最终需要一个数组或者更复杂的结构(想想我们不仅关心商品种类,还关心商品购买的数量)来存储。作者虽然不建议在数仓维度建模时进行规范化,但是作者觉得第一范式(也就是每个列不要存储复杂结构如数组、对象等)的底线还是要守住的。
OK,那你可能还会问,如果我要以订单作为分析对象该怎么办?有的分析只能以订单为粒度进行,比方说满减优惠。这时可以给商品订单加上订单维度,用以保存订单的相关信息,比如订单总金额,订单收货地址,订单优惠等。然后订单维度和商品订单事实表以外键关联。
不过一切设计在没有说清楚场景的时候,都很难比较。刚刚的分析是假定需要有很多基于商品的分析,假如情况并非如此,可能结果也不一样。
事实表有三种基本种类
那么可能有人会问了,3#看起来就像是把几类不同的事实粘合在一起,能用多个1#类事实表替代吗?
作者提到,SQL的跨行分析能力很差。假如我们希望找出第一阶段和第二阶段之间的时间差大于5天的慢流程,那就需要做一次多事实表JOIN得到临时表,然后再在临时表上做进一步分析。如果某些事实本身就具有3#可应用的模式,那何必强行用1#呢?
事实表还有一些特殊形式,比如
自然键通常指的是维度信息中具有现实意义的某些列,它们能唯一指定维度表中的一行(或者某些行,在需要保留更改记录的情况下)。自然键非常直观,比方说用产品SKU作为商品表的主键,事实表都通过SKU与商品表关联。那么为什么作者建议不要使用自然键作为维度表主键呢?主要原因大概有:
我们知道在SQL数据库建模领域有第一范式、第二范式、第三范式等。而在数仓建模领域,通常只遵循第一范式,只要每个列都存储基础类型就可以。换个术语来说,数仓领域通常是星型模型(事实表在中央,与一系列维度表关联,就像从一个点发出多条射线一样),而不是雪花模型(事实表在中央与多个维度表关联,维度表还和一系列维度表关联,就像雪花,每个子结构都相似)。
为什么需要避免过度规范化?主要从性能和简单性出发考虑:
如果有的使用者担心非规范化存储了很多重复值,浪费了很多空间,作者的意见是,维度表的容量相对于事实表少了几个数量级,因此无需在意空间的浪费。
作者在本书中还反复提到了支架表。支架表的想法和雪花模型比较类似,大概就是把维度表中一些重复的属性抽成单独的维度表,并与主维度表关联。作者强调支架表可以用,但是尽量不要用,否则可能是走在过度规范化的路上——作者既然叫它“支架”而不是雪花,说明一般来说主维度也就和另一个副维度相关联。如果关联了好几个副维度,那可就不是支架而是真雪花了。
什么时候维度表会形同事实表呢?通常是事实表和维度表都使用了同一主键的时候。比方说一个订单表存储了用户的一次购买行为(以订单为粒度),而设计者觉得应该把订单信息(比如订单号、订单日期、订单金额等我就瞎说一通了)单独放到一个订单维度表里,而事实表里存放订单维度键、用户维度键等。这时会发现,事实表和维度表的行数是相同的。
也就是说,当看到维度表和事实表一样大时,就要觉得有点不对劲了。这两个表实际上是同一个表,因此解决方法是把两个表的字段合一(因为它们粒度相同,主键相同,因此合一不会有任何问题),然后再考虑抽取维度。可以参考第11章-电信中的评审例子来理解这一原则
第五章主要讲解了SCD建模问题。书中称之为缓慢变化维度,但实际上只关注维度变化的问题,而不只是“缓慢”变化维度。从这一章之后,书中会时不时提到“第二类变化维度”这种词,指的就是第五章介绍的这些SCD建模方法
事件表的修改通常是追加新的事件来增加行数,修改一般也是订正错误的事件,所以一般来说,修改历史事件不太需要很多讨论。但维度表通常会被多个事实表、多行关联,因此维度表的修改要考虑的问题更多。比如说
上面大致介绍了变化维度建模需要考虑的事项,接下来介绍具体的建模方法。需要注意从这一章开始
这些可以直接看11.2节“设计评审的一般性考虑”和16.9节 “需要避免的常见维度建模错误”,作者做了比较好的总结。
工作之后也没有太多时间看书或者写总结,这篇笔记也写得比较粗糙。如果有什么说得不对的或者希望讨论的也可以直接提出来