数据仓库中有一种数据存储模型,用于维护历史状态以及最新状态,反映某一Key的历史变化演进过程,该种模型叫做缓慢变化维,简称SCD(Slowly Changing Dimensions),通常所说的拉链表其实属于缓慢变化维的一种实现方式,拉链表通过增加一列维度信息维护拉链状态,他们之间稍微有所不同,具体区别如下:
相同点:
缓慢变化维度,简称SCD (Slowly Changing Dimensions)和拉链表都是为了存储非静态数据的历史变化状态。
不同点:
拉链表有开链和闭链过程,总一条数据明确表示最新状态(例如常见的 end_date=9999-12-31 00:00:00),针对的大都是事实表。
缓慢变化维,没有开链与闭链过程,只是单纯的记录数据的变化过程,解决的也都是维度表的存储,反映维度的数据历史演进过程。
具体举例如下:
1.1 缓慢变化维
套餐类型 | 通话时间(举例) | 创建时间 | 更新时间 |
---|---|---|---|
A01 | 400分钟 | 2019年12月31日 | 2019年12月31日 |
A02 | 600分钟 | 2019年12月31日 | 2019年12月31日 |
A01 | 800分钟 | 2019年12月31日 | 2019年12月31日 |
A02 | 1000分钟 | 2019年12月31日 | 2019年12月31日 |
A01 | 1200分钟 | 2019年12月31日 | 2019年12月31日 |
缓慢变化维的变化相对拉链表要简单很多,直接插入最新状态即可,例如A02套餐内容中通话时长更新为2000分钟后,变化后的结果如下:
套餐类型 | 通话时间(举例) | 创建时间 | 更新时间 |
---|---|---|---|
A01 | 400分钟 | 2019年12月31日 | 2019年12月31日 |
A02 | 600分钟 | 2019年12月31日 | 2019年12月31日 |
A01 | 800分钟 | 2019年12月31日 | 2019年12月31日 |
A02 | 1000分钟 | 2019年12月31日 | 2019年12月31日 |
A01 | 1200分钟 | 2019年12月31日 | 2019年12月31日 |
A02 | 2000分钟 | 2019年12月31日 | 2022年05月31日 |
1.2 拉链表
在实现上增加了失效时间字段,用于表示保存变化后的最新状态值
用户ID | 套餐类型 | 生效时间 | 失效时间 |
---|---|---|---|
U0001 | A01 | 2020年1月1日 | 2020年1月1日 |
U0002 | A02 | 2020年6月1日 | 2020年6月1日 |
U0001 | A02 | 2021年1月2日 | 2022年1月2日 |
U0002 | A01 | 2021年6月2日 | 9999年12月31日 |
U0001 | A01 | 2022年1月2日 | 9999年12月31日 |
具体实现如下:例如用户U0001更新了新套餐A02,操作如下:
将当前最新状态数据更新,也就是闭链操作
变更为:
将最新状态数据插入,也就是开链操作
最终结果为:
用户ID | 套餐类型 | 生效时间 | 失效时间 |
---|---|---|---|
U0001 | A01 | 2020年1月1日 | 2020年1月1日 |
U0002 | A02 | 2020年6月1日 | 2020年6月1日 |
U0001 | A02 | 2021年1月2日 | 2022年1月2日 |
U0002 | A01 | 2021年6月2日 | 9999年12月31日 |
U0001 | A01 | 2022年1月2日 | 2022年5月31日 |
U0001 | A02 | 2022年5月31日 | 9999年12月31日 |
2 使用场景
数据仓库的数据模型设计过程中,经常会遇到如下这几种表的设计:
-
比如有一张用户表,数据量很大,50多个字段,大约10亿条记录,即使使用ORC压缩,单张表的存储也会超过100G,在hdfs中要是三副本存储那就会更大。
-
表中的部分字段会被更新,如用户联系方式,产品的描述信息,订单状态。
-
需要查看某一个时间点或者时间段的历史快照信息,比如看某一订单在历史某一时间点的状态。
-
表中的记录变化的比例和频率不是很大比如,总共有10亿的用户,每天新增和发生变化的有200w左右,变化的比例占到很小。
那么对于这种表我该如何设计呢?下面有几种方案可选:
-
方案一:每天只留最新的一份,比如我们每天用 Sqoop 抽取最新的一份全量数据到 Hive 中。
-
方案二:每天保留一份全量的切片数据。
-
方案三:使用拉链表。
3 为什么使用拉链表
现在我们对前面提到的三种进行逐个的分析。
方案一 每天只保留最新数据
这种方案就不用多说了,实现起来很简单,每天 Drop 掉前一天的数据,重新抽一份最新的。
优点 :很明显,节省空间,一些普通的使用也很方便,不用在选择表的时候加一个时间分区什么的。
缺点 :同样明显,没有历史数据,先翻翻旧账只能通过其它方式,比如从流水表里面抽。
方案二 每天保留一份全量切片数据
每天一份全量的切片是一种比较稳妥的方案,而且历史数据也在。
缺点: 就是存储空间占用量太大太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费。
当然我们也可以做一些取舍,比如只保留近一个月的数据。但是,需求是无耻的,数据的生命周期不是我们能完全左右的,你会发现,存储周期可能会从 30 天变为 90 天,然后再从 90 天变为 1 年,然后需要永久保存。
拉链表
拉链表在使用上基本兼顾了我们的需求。
首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。
其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。所以在一些场景下,拉链表是能解决很多问题的。