赞
踩
本文将会谈一谈在数据仓库中拉链表相关的内容,包括它的原理、设计、以及在我们大数据场景下的实现方式。
全文由下面几个部分组成:
0x01 什么是拉链表
拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。
我们先看一个示例,这就是一张拉链表,存储的是用户的最基本信息以及每条记录的生命周期。我们可以使用这张表拿到最新的当天的最新数据以及之前的历史数据。
我们暂且不对这张表做细致的讲解,后文会专门来阐述怎么来设计、实现和使用它。
拉链表的使用场景
在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:
那么对于这种表我该如何设计呢?下面有几种方案可选:
为什么使用拉链表
现在我们对前面提到的三种进行逐个的分析。
方案一
这种方案就不用多说了,实现起来很简单,每天drop掉前一天的数据,重新抽一份最新的。
优点很明显,节省空间,一些普通的使用也很方便,不用在选择表的时候加一个时间分区什么的。
缺点同样明显,没有历史数据,要想翻旧账只能通过其它方式,比如从流水表里面抽。
方案二
每天一份全量的切片是一种比较稳妥的方案,而且历史数据也在。
缺点就是存储空间占用量太大太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费,这点我感触还是很深的……
当然我们也可以做一些取舍,比如只保留近一个月的数据?但是,需求是无耻的,数据的生命周期不是我们能完全左右的。
拉链表
拉链表在使用上基本兼顾了我们的需求。
首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。
其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。
所以我们还是很有必要来使用拉链表的。
0x02 拉链表的设计和实现
如何设计一张拉链表
下面我们来举个栗子详细看一下拉链表。
我们先看一下在Mysql关系型数据库里的user表中信息变化。
在2017-01-01这一天表中的数据是:
在2017-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:
在2017-01-03这一天表中的数据是, 用户004和005资料进行了修改,006是新增用户:
如果在数据仓库中设计成历史拉链表保存该表,则会有下面这样一张表,这是最新一天(即2017-01-03)的数据:
说明
在Hive中实现拉链表
在现在的大数据场景下,大部分的公司都会选择以Hdfs和Hive为主的数据仓库架构。目前的Hdfs版本来讲,其文件系统中的文件是不能做改变的,也就是说Hive的表只能进行删除和添加操作,而不能进行update。基于这个前提,我们来实现拉链表。
还是以上面的用户表为例,我们要实现用户的拉链表。在实现它之前,我们需要先确定一下我们有哪些数据源可以用。
而且我们要确定拉链表的时间粒度,比如说拉链表每天只取一个状态,也就是说如果一天有3个状态变更,我们只取最后一个状态,这种天粒度的表其实已经能解决大部分的问题了。
另外,补充一下每日的用户更新表该怎么获取,据笔者的经验,有以下方式拿到或者间接拿到每日的用户增量,因为它比较重要,所以详细说明:
拉链表实现方式一:
ods层的user表
现在我们来看一下我们ods层的用户资料切片表的结构:
ods层的user_update表
然后我们还需要一张用户每日更新表,前面已经分析过该如果得到这张表,现在我们假设它已经存在。
拉链表
现在我们创建一张拉链表:
实现sql语句
然后初始化的sql就不写了,其实就相当于是拿一天的ods层用户表过来就行,我们写一下每日的更新语句。
现在我们假设我们已经已经初始化了2017-01-01的日期,然后需要更新2017-01-02那一天的数据,我们有了下面的Sql。
然后把两个日期设置为变量就可以了。
拉链表实现方式二:
操作型数据库的用户表结构:
这里我们假设ods.user表的业务主键为user_num+mobile作为联合主键。
每天增量抽取的用户表结构和抽取条件:
1)表结构和上面的表结构保持一致,我们取表名为ods.user_update
2)增量抽取条件:select * from ods.user where last_modify_date = '$date'
拉链表
现在我们创建一张拉链表:
实现sql
1)
merge into dws.user_his tar
using
(
select user_num,mobile from ods.user_update
) sou on tar.user_num=sou.user_num and tar.mobile=sou.mobile and tar.t_start_date < '$date' and tar.t_end_date > '$date'
when matched then
update set tar.t_end_date='9999-12-31'
按照主键筛选,在dws.user_his表中出现过的并且现在为有效数据的,全部更新为闭链数据。
2)
比如我们要1月2号的数据,取出来的数据为
select from user where t_start_date <= ‘2017-01-02’ and t_end_date >= ‘2017-01-02’
与1月2号数据完全一致。
0x03 补充
好了,我们分析了拉链表的原理、设计思路、并且在Hive环境下实现了一份拉链表,下面对拉链表做一些小的补充。
拉链表和流水表
流水表存放的是一个用户的变更记录,比如在一张流水表中,一天的数据中,会存放一个用户的每条修改记录,但是在拉链表中只有一条记录。
这是拉链表设计时需要注意的一个粒度问题。我们当然也可以设置的粒度更小一些,一般按天就足够。
查询性能
拉链表当然也会遇到查询性能的问题,比如说我们存放了5年的拉链数据,那么这张表势必会比较大,当查询的时候性能就比较低了,个人认为两个思路来解决:
4 拉链表回滚
4.1 具体操作方案
假设恢复到t天之前的数据,即未融合t天数据之前的拉链表,假设标记的开始日期和结束日期分别为s、t,具体分析如下:
1 当t-1>e时,s数据、e数据在t天之前产生,保留即可 2 当t-1=e时,e数据在t天产生,需修改 3 当s<t<=e时,e数据在t+n天产生,需修改 4 当s>=t时,s数据、e数据在t+n天产生,删除即可
具体例子:
spark-sql> select * from t_dw_orders_his order by orderid,dw_start_date; 1 2015-08-18 2015-08-18 创建 2015-08-18 2015-08-21 1 2015-08-18 2015-08-22 支付 2015-08-22 2015-08-22 1 2015-08-18 2015-08-23 完成 2015-08-23 9999-12-31 2 2015-08-18 2015-08-18 创建 2015-08-18 2015-08-21 2 2015-08-18 2015-08-22 完成 2015-08-22 9999-12-31 3 2015-08-19 2015-08-21 支付 2015-08-19 2015-08-20 3 2015-08-19 2015-08-21 支付 2015-08-21 2015-08-22 3 2015-08-19 2015-08-23 完成 2015-08-23 9999-12-31 4 2015-08-19 2015-08-21 完成 2015-08-19 2015-08-20 4 2015-08-19 2015-08-21 完成 2015-08-21 9999-12-31 5 2015-08-19 2015-08-20 支付 2015-08-19 2015-08-22 5 2015-08-19 2015-08-23 完成 2015-08-23 9999-12-31 6 2015-08-20 2015-08-20 创建 2015-08-20 2015-08-21 6 2015-08-20 2015-08-22 支付 2015-08-22 9999-12-31 7 2015-08-20 2015-08-21 支付 2015-08-20 2015-08-20 7 2015-08-20 2015-08-21 支付 2015-08-21 9999-12-31 8 2015-08-21 2015-08-21 创建 2015-08-21 2015-08-21 8 2015-08-21 2015-08-22 支付 2015-08-22 2015-08-22 8 2015-08-21 2015-08-23 完成 2015-08-23 9999-12-31 9 2015-08-22 2015-08-22 创建 2015-08-22 9999-12-31 10 2015-08-22 2015-08-22 支付 2015-08-22 9999-12-31 11 2015-08-23 2015-08-23 创建 2015-08-23 9999-12-31 12 2015-08-23 2015-08-23 创建 2015-08-23 9999-12-31 13 2015-08-23 2015-08-23 支付 2015-08-23 9999-12-31
比如在插入2015-08-23的数据后,回滚2015-08-22的数据,使拉链表与2015-08-21的一致,具体操作过程如下
1 增加临时表t_dw_orders_his_tmp1,用来记录t-1>e的数据 CREATE TABLE t_dw_orders_his_tmp1 AS SELECT orderid, createtime, modifiedtime, status, dw_start_date, dw_end_date FROM t_dw_orders_his WHERE dw_end_date < '2015-08-21'
3 2015-08-19 2015-08-21 支付 2015-08-19 2015-08-20 4 2015-08-19 2015-08-21 完成 2015-08-19 2015-08-20 7 2015-08-20 2015-08-21 支付 2015-08-20 2015-08-20
2 增加临时表t_dw_orders_his_tmp2,用来记录t-1=e的数据 CREATE TABLE t_dw_orders_his_tmp2 AS SELECT orderid, createtime, modifiedtime, status, dw_start_date, '9999-12-31' AS dw_end_date FROM t_dw_orders_his WHERE dw_end_date = '2015-08-21'
1 2015-08-18 2015-08-18 创建 2015-08-18 9999-12-31 2 2015-08-18 2015-08-18 创建 2015-08-18 9999-12-31 6 2015-08-20 2015-08-20 创建 2015-08-20 9999-12-31 8 2015-08-21 2015-08-21 创建 2015-08-21 9999-12-31
3 增加临时表t_dw_orders_his_tmp3,用来记录s<t<=e的数据 CREATE TABLE t_dw_orders_his_tmp3 AS SELECT orderid, createtime, modifiedtime, status, dw_start_date, '9999-12-31' dw_end_date FROM t_dw_orders_his WHERE dw_start_date < '2015-08-22' AND dw_end_date >= '2015-08-22'
3 2015-08-19 2015-08-21 支付 2015-08-21 9999-12-31 4 2015-08-19 2015-08-21 完成 2015-08-21 9999-12-31 5 2015-08-19 2015-08-20 支付 2015-08-19 9999-12-31 7 2015-08-20 2015-08-21 支付 2015-08-21 9999-12-31
4 所有数据插入新表t_dw_orders_his_new CREATE TABLE t_dw_orders_his_new AS SELECT * FROM t_dw_orders_his_tmp1 UNION ALL SELECT * FROM t_dw_orders_his_tmp2 UNION ALL SELECT * FROM t_dw_orders_his_tmp3
1 2015-08-18 2015-08-18 创建 2015-08-18 9999-12-31 2 2015-08-18 2015-08-18 创建 2015-08-18 9999-12-31 3 2015-08-19 2015-08-21 支付 2015-08-19 2015-08-20 3 2015-08-19 2015-08-21 支付 2015-08-21 9999-12-31 4 2015-08-19 2015-08-21 完成 2015-08-19 2015-08-20 4 2015-08-19 2015-08-21 完成 2015-08-21 9999-12-31 5 2015-08-19 2015-08-20 支付 2015-08-19 9999-12-31 6 2015-08-20 2015-08-20 创建 2015-08-20 9999-12-31 7 2015-08-20 2015-08-21 支付 2015-08-20 2015-08-20 7 2015-08-20 2015-08-21 支付 2015-08-21 9999-12-31 8 2015-08-21 2015-08-21 创建 2015-08-21 9999-12-31
与原数据一致,验证无错
4.2 备用方案
可以采用备份的方案,保证无误和可行。(保存增量数据,并对t_dw_orders_his表每个月备份一次全量数据。如需回滚,最多重跑30天数据即可)
0xFF 总结
我们在这篇文章里面详细地分享了一下和拉链表相关的知识点,但是仍然会有一会遗漏。欢迎交流。
在后面的使用中又有了一些心得,补充进来:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。