当前位置:   article > 正文

图解数据仓库之拉链表,超简单!_数据拉链实时形成

数据拉链实时形成

 

1.背景分析

 在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:

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

拉链表可以解决上述数据缓慢化场景问题,既能保存历史状态,又能尽量节省空间资源,数仓中某些维度缓慢变化的应用场景可以使用缓慢变化维拉链表。

2.图解拉链表形成过程

1)下图为业务后台数据库中每日的数据变化情况,每天只保留客户最新手机号码。

2)根据最后修改时间字段从业务后台数据库抽取最新被修改数据,形成分区表。

 

3)拉链表的形成过程(重点!!!)

 

3.过程分析

首先根据上面的分析过程,对sql比较熟悉的小伙伴估计已经能够想出整套的解决方案了。一般在工作中,和业务人员沟通之后,自己如果能清楚的描述出业务过程,基本上需求已经实现一大半了,接下来就是写代码啦!

1)整体思路分析

  1. 从业务后台数据库同步到数据仓库,形成初始化拉链表
  2. 每天根据数据修改时间字段抽取数据,形成日分区表
  3. 根据初始化拉链表(前一天)和日分区表(当日)形成当日拉链表

2)进一步思考

  1. 何时更新拉链表?答:业务数据发生变化时候,即当日分区表有数据就可以更新形成新的拉链表。
  2. 可以将上述三个步骤写成一个存储过程,后续直接调用即可生成拉链表,当然在大数据平台可已开发定时任务更新拉链表。
  3. 如何回滚拉链表,即回到前面的历史状态?答:这里主要应对更新拉链表出错的情况,后续再写另外的文章阐述。
  4. 如果t_end_time默认可以不是‘9999-12-31’这种永久有效状态,意味着用户的数据是有时效的,由此可以联想到用户购买会员的时效情景,这里拉链表更新时候需要注意一下会与普通t_end_time = '9999-12-31'的数据会有所不同。

4.代码实现

1)算法思想

  1. 1、采集当日全量数据到ND(NewDay)表;
  2. 2、可从历史表中取出昨日全量数据存储到OD(OldDay)表;
  3. 3、(ND-OD)就是当日新增和变化的数据,也就是当天的增量,用W_I表示;
  4. 4、(OD-ND)为状态到此结束需要封链的数据,用W_U表示;
  5. 5、将W_I表的内容全部插入到历史表中,这些是新增记录,start_date为当天,而end_date为max值;
  6. 6、对历史表进行W_U部份的更新操作,start_date保持不变,而end_date改为当天,也就是关链操作

 2)代码实现

    1. ods层user表结构

  1. CREATE EXTERNAL TABLE ods.user (
  2. user_num STRING COMMENT '用户编号',
  3. mobile STRING COMMENT '手机号码',
  4. reg_date STRING COMMENT '注册日期',
  5. modified_time STRING COMMENT '更新日期'
  6. COMMENT '用户资料表'
  7. PARTITIONED BY (dt string)
  8. ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
  9. STORED AS ORC
  10. LOCATION '/ods/user';
  11. )

    2. ods层user_update表结构

  1. CREATE EXTERNAL TABLE ods.user_update (
  2. user_num STRING COMMENT '用户编号',
  3. mobile STRING COMMENT '手机号码',
  4. reg_date STRING COMMENT '注册日期',
  5. modified_time STRING COMMENT '更新日期'
  6. COMMENT '每日用户资料更新表'
  7. PARTITIONED BY (dt string)
  8. ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
  9. STORED AS ORC
  10. LOCATION '/ods/user_update';
  11. )

    3. 拉链表结构

  1. CREATE EXTERNAL TABLE dws.user_his (
  2. user_num STRING COMMENT '用户编号',
  3. mobile STRING COMMENT '手机号码',
  4. reg_date STRING COMMENT '注册日期',
  5. modified_time STRING COMMENT '更新日期',
  6. t_start_date STRING COMMENT '数据开始时间',
  7. t_end_date STRING COMMENT '数据结束时间'
  8. COMMENT '用户资料拉链表'
  9. ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
  10. STORED AS ORC
  11. LOCATION '/dws/user_his';
  12. )

    4. 拉链表从2017-01-01更新到2017-01-02

  1. INSERT OVERWRITE TABLE dws.user_his
  2. SELECT * FROM
  3. (
  4. SELECT A.user_num,
  5. A.mobile,
  6. A.reg_date,
  7. A.modified_time
  8. A.t_start_time,
  9. CASE
  10. WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'
  11. ELSE A.t_end_time
  12. END AS t_end_time
  13. FROM dws.user_his AS A
  14. LEFT JOIN ods.user_update AS B
  15. ON A.user_num = B.user_num
  16. UNION
  17. SELECT C.user_num,
  18. C.mobile,
  19. C.reg_date,
  20. C.modified_time,
  21. '2017-01-02' AS t_start_time,
  22. '9999-12-31' AS t_end_time
  23. FROM ods.user_update AS C
  24. ) AS T

     5. 拉链表从2017-01-02更新到2017-01-03

  1. INSERT OVERWRITE TABLE dws.user_his
  2. SELECT * FROM
  3. (
  4. SELECT A.user_num,
  5. A.mobile,
  6. A.reg_date,
  7. A.modified_time
  8. A.t_start_time,
  9. CASE
  10. WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-02'
  11. ELSE A.t_end_time
  12. END AS t_end_time
  13. FROM dws.user_his AS A
  14. LEFT JOIN ods.user_update AS B
  15. ON A.user_num = B.user_num
  16. UNION
  17. SELECT C.user_num,
  18. C.mobile,
  19. C.reg_date,
  20. C.modified_time,
  21. '2017-01-03' AS t_start_time,
  22. '9999-12-31' AS t_end_time
  23. FROM ods.user_update AS C
  24. ) AS T

     6. 替换日期为变量,比如今日是2017-01-03,数仓中可以更新2017-01-02数据,此时$date  = 2017-01-02

  1. INSERT OVERWRITE TABLE dws.user_his
  2. SELECT * FROM
  3. (
  4. SELECT A.user_num,
  5. A.mobile,
  6. A.reg_date,
  7. A.modified_time
  8. A.t_start_time,
  9. CASE
  10. WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN date_add($date,-1)
  11. ELSE A.t_end_time
  12. END AS t_end_time
  13. FROM dws.user_his AS A
  14. LEFT JOIN ods.user_update AS B
  15. ON A.user_num = B.user_num
  16. UNION
  17. SELECT C.user_num,
  18. C.mobile,
  19. C.reg_date,
  20. C.modified_time,
  21. $date AS t_start_time,
  22. '9999-12-31' AS t_end_time
  23. FROM ods.user_update AS C
  24. ) AS T

4.评价:拉链表对比全量表和分区表

    1. 下图展示拉链表,分区表和全量表的结果

    2.三种类型的表各自的对比如下,当然每种表有每种表各自适合的使用场景,具体问题要具体分析!

写在最后

    文中若有不正之处,望请指正!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/255202
推荐阅读
相关标签
  

闽ICP备14008679号