数据仓库拉链表

1.什么是拉链表

拉链表是根据数据存储得方式而定义的,记录每天信息的生命周期,一旦一条记录的生命周期结束,就开始新的一条记录,并把当前日期作为生效日期。

 

2.为什么要做拉链表

  1. 有一些表的数据量很大,比如一张用户表,大约10亿条记录,50个字段,这种表,即使使用ORC压缩,单张表的存储也会超过100G,在HDFS使用双备份或者三备份的话就更大一些。
  2. 表中的部分字段会被update更新操作,如用户联系方式,产品的描述信息,订单的状态等等。
  3. 需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态。
  4. 表中的记录变化的比例和频率不是很大,比如,总共有10亿的用户,每天新增和发生变化的有200万左右,变化的比例占的很小。

那么对于这种表我该如何设计呢?下面有几种方案可选:

  方案一:每天只留最新的一份,比如我们每天用Sqoop抽取最新的一份全量数据到Hive中。

  方案二:每天保留一份全量的切片数据。

  方案三:使用拉链表

 
我们对上面三个方案分析:
  

  方案一

  这种方案就不用多说了,实现起来很简单,每天drop掉前一天的数据,重新抽一份最新的。

  优点很明显,节省空间,一些普通的使用也很方便,不用在选择表的时候加一个时间分区什么的。

  缺点同样明显,没有历史数据,先翻翻旧账只能通过其它方式,比如从流水表里面抽。

  方案二

  每天一份全量的切片是一种比较稳妥的方案,而且历史数据也在。

  缺点就是存储空间占用量太大太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费,这点我感触还是很深的......

  当然我们也可以做一些取舍,比如只保留近一个月的数据?但是,需求是无耻的,数据的生命周期不是我们能完全左右的。

  拉链表

  拉链表在使用上基本兼顾了我们的需求。

  首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。

  其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。


 3.如何做拉链表

拉链表制作流程图

4.拉链表制作

创建拉链表

 1 drop table if exists dwd_order_info_his;
 2 create external table dwd_order_info_his(
 3     `id` string COMMENT '订单编号',
 4     `total_amount` decimal(10,2) COMMENT '订单金额',
 5     `order_status` string COMMENT '订单状态',
 6     `user_id` string COMMENT '用户id' ,
 7     `payment_way` string COMMENT '支付方式',
 8     `out_trade_no` string COMMENT '支付流水号',
 9     `create_time` string COMMENT '创建时间',
10     `operate_time` string COMMENT '操作时间',
11     `start_date`  string COMMENT '有效开始日期',
12     `end_date`  string COMMENT '有效结束日期'
13 ) COMMENT '订单拉链表'
14 stored as parquet
15 location '/warehouse/gmall/dwd/dwd_order_info_his/'
16 tblproperties ("parquet.compression"="snappy");

初始化拉链表

 1 insert overwrite table dwd_order_info_his
 2 select
 3     id,
 4     total_amount,
 5     order_status,
 6     user_id,
 7     payment_way,
 8     out_trade_no,
 9     create_time,
10     operate_time,
11     '2019-02-13',
12     '9999-99-99'
13 from ods_order_info oi
14 where oi.dt='2019-02-13';

  如何获得每日变动表

  (1)最好表内有创建时间和变动时间

  (2)如果没有,可以利用第三方工具监控比如canal,监控MySQL的实时变化进行记录(麻烦)。

  (3)逐行对比前后两天的数据, 检查md5(concat(全部有可能变化的字段))是否相同(low)

  (4)要求业务数据库提供变动流水

创建临时表

 1 drop table if exists dwd_order_info_his_tmp;
 2 create table dwd_order_info_his_tmp( 
 3     `id` string COMMENT '订单编号',
 4     `total_amount` decimal(10,2) COMMENT '订单金额', 
 5     `order_status` string COMMENT '订单状态', 
 6     `user_id` string COMMENT '用户id' ,
 7     `payment_way` string COMMENT '支付方式',  
 8     `out_trade_no` string COMMENT '支付流水号',  
 9     `create_time` string COMMENT '创建时间',  
10     `operate_time` string COMMENT '操作时间',
11     `start_date`  string COMMENT '有效开始日期',
12     `end_date`  string COMMENT '有效结束日期'
13 ) COMMENT '订单拉链临时表'
14 stored as parquet
15 location '/warehouse/gmall/dwd/dwd_order_info_his_tmp/'
16 tblproperties ("parquet.compression"="snappy");

合并数据到临时表

 //dwd_order_info为存储新增及变化的表
 1 insert overwrite table dwd_order_info_his_tmp
 2 select * from 
 3 (
 4 select 
 5 id,
 6     total_amount,
 7     order_status,
 8     user_id,
 9     payment_way,
10     out_trade_no,
11     create_time,
12     operate_time,
13     '2019-02-14' start_date,
14     '9999-99-99' end_date
15 from dwd_order_info where dt='2019-02-14'
16 
17 union all 
18 select oh.id,
19     oh.total_amount,
20     oh.order_status,
21     oh.user_id,
22     oh.payment_way,
23     oh.out_trade_no,
24     oh.create_time,
25     oh.operate_time,
26     oh.start_date,
27     if(oi.id is null, oh.end_date, date_add(oi.dt,-1)) end_date
28 from dwd_order_info_his oh left join 
29      (
30 select
31 *
32 from dwd_order_info
33 where dt='2019-02-14'
34 ) oi
35      on oh.id=oi.id and oh.end_date='9999-99-99'  
36 )his 
37 order by his.id, start_date;

覆盖原拉链表

insert overwrite table dwd_order_info_his 

查询性能

拉链表当然也会遇到查询性能的问题,比如说我们存放了5年的拉链数据,那么这张表势必会比较大,当查询的时候性能就比较低了,个人认为两个思路来解决:

  1. 在一些查询引擎中,我们对start_date和end_date做索引,这样能提高不少性能。
  2. 保留部分历史数据,比如说我们一张表里面存放全量的拉链表数据,然后再对外暴露一张只提供近3个月数据的拉链表。



参考文章:https://www.jianshu.com/p/799252156379

猜你喜欢

转载自www.cnblogs.com/huangguoming/p/12750895.html