目录
1、什么是拉链表
2、拉链表是怎么存储的
3、Hive中拉链表的存储过程
拉链表是我们存储数据时为了处理某些情况而设计的一种表结构,是一种存储数据的方法。
在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:
1.有一些表的数据量很大,比如一张用户表,大约10亿条记录,50个字段,这种表,即使使用ORC压缩,单张表的存储也会超过100G,在HDFS使用双备份或者三备份的话就更大一些。
2.表中的部分字段会被update更新操作,如用户联系方式,产品的描述信息,订单的状态等等。 应用时需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态。
3.表中的记录变化的比例和频率不是很大,比如,总共有10亿的用户,每天新增和发生变化的有200万左右,变化的比例占的很小。
这种情况下,我们如果每天保存一份全量数据,但是数据量很大,每天都抽一份全量数据,再有钱的公司可能也支付不起数据库的费用,而且表中记录变化的比例和频率都不是很大,全量抽取数据库中会有大量的重复数据,没有意义;我们如果每天先drop掉前一天的数据,再重新抽取一份新的,这样解决了物理存储费用的问题,但是这样不能查看某一个订单在历史某一个时间点的状态,不能满足业务需求;所以我们需要是当数据发生变化时仅对该数据进行记录并修改的,可以存储历史数据的存储方法。有人就设计出了拉链表。
为了更直观的感受拉链表,请仔细观察下面表的变化,注意start_dt和end_dt是拉链表的精髓。
开始的时候
UserID |
UserName |
Height |
start_dt |
end_dt |
000000001 |
张三 |
176 |
2015-05-14 |
2999-12-31 |
000000002 |
李四 |
170 |
2015-05-14 |
2999-12-31 |
UserID为000000001的这条数据的Height字段发生变化
UserID |
UserName |
Height |
start_dt |
end_dt |
000000001 |
张三 |
176 |
2015-05-14 |
2016-07-24 |
000000001 |
张三 |
180 |
2016-07-24 |
2999-12-31 |
000000002 |
李四 |
170 |
2015-05-14 |
2999-12-31 |
UserID为000000001的这条数据的UserName字段发生变化
UserID |
UserName |
Height |
start_dt |
end_dt |
000000001 |
张三 |
176 |
2015-05-14 |
2016-07-24 |
000000001 |
张三 |
180 |
2016-07-24 |
2018-10-13 |
000000001 |
SanZhang |
180 |
2018-10-13 |
2999-12-31 |
000000002 |
李四 |
170 |
2015-05-14 |
2999-12-31 |
应该都能感受的到,start_dt和end_dt字段,在数据发生变化的时候就像一条拉链,所以都叫它拉链表。
其实明白了什么是拉链表,它的存储就非常简单。
首先,我们要和前一日的数据进行对比,看看哪一条数据发生了变化,我们要看看它是增加了新的数据?修改了数据,还是删除了数据。
如果是新增的数据,我们仅需要像新建拉链表时一样,给数据添加一个开始时间和结束时间,并将数据插入拉链表。开始时间就是新增数据当天,结束时间一般都是2999-12-31(有的公司觉得2999-12-31不够久,用9999-12-31,其实都是一样的);
如果是修改的数据,我们除了需要像上面将新数据插入拉链表,还需要找到该数据对应的历史数据,因为历史数据的结束日期还是2999-12-31呢,我们要把它改成修改数据当天的日期;
如果时删除了数据,那么这个数据在删除当天就无效了,我们仅仅需要将结束日期改成删除当天即可。
一般的,我们把插入数据的的动作叫做开链,把修改结束日期的动作叫做关链。
由于Hive的特点是批量处理数据,在HQL中,通常不使用Update对数据进行修改。
所以我们在Hive上建拉链表,写的HQL和SQL还是有一定的差别的,由于2只是说了个思路,所以这里我说的详细点 。
假设我现在又两个表,ods_source和dw_target,我要将数据从ods_source表存储到dw_target表。
第一步,我们将ods_source和dw_target的非历史数据进行对比,得到新增的数据和修改的数据,并进行开链,存在一个临时表中。
create table if not exists tmp_increment as
select *,hash(a.*) as hashcode,date'2018-10-13' as strat_dt,date'2999-12-31' as end_dt
from osd_source a
left join dw_target b
on a.主键=b.主键
and b.end_dt=date'2999-12-31'
and hash(a.*)=b.hashcode
where b.其它字段 is null
其中hashcode是为了比出修改的数据,如果不懂请自行解决,我就不再细说了。
第二步,我们将第一步中修改的数据进行关链,存在一个临时表中。
create table if not exists tmp_update as
select *,hash(*) as hashcode,strat_dt,date'2018-10-13' as end_dt
from tmp_increment a
inner join dw_target b on a.主键=b.主键 and b.end_dt=date'2999-12-31'
第三步,我们ods_source和dw_target的非历史数据进行对比,得到ods_source表中删除的数据,或者是失效的数据,并进行关链,存在一个临时表。
create table if not exists tmp_delete as
select *,hash(*) as hashcode,strat_dt,date'2999-12-31' as end_dt
from dw_target a
left join osd_source b on a.主键=b.主键 and a.end_dt=date'2999-12-31'
where b.其它字段 is null
第四步,我们将ods_source和dw_target的非历史数据进行对比,得到没有变化的数据,存在一个临时表。
create table if not exists tmp_nochang as
select *,hash(*) as hashcode,date'2018-10-13' as strat_dt,date'2999-12-31' as end_dt
from osd_source a
left join dw_target b on a.主键=b.主键 and b.end_dt=date'2999-12-31'
where b.其它字段 is not null
第五步,我们找出dw_target表中的历史数据,存在一个临时表。
create table if not exists tmp_history as
select * from dw_target where end_dt <> date'2999-12-31'
第六步,我们将前五步的临时表合在一起,形成一个新的拉链表,并drop掉临时表和原来的拉链表,这就是Hive中拉链表的做法。
以上都是本人自己整理的,如果有错误,请私信我或者在下面评论指出,谢谢!!!