首页 > 新闻资讯
3000字解说数据仓库的拉链表
来源:nba直播吧    发布时间:2024-01-27 18:42:03

  拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。

  我们先看一个示例,这就是一张拉链表,存储的是用户的最基础信息以及每条记录的生命周期。我们大家可以使用这张表拿到最新的当天的最新数据及之前的历史数据。

  我们暂且不对这张表做细致的讲解,后文会专门来阐述怎么来设计、实现和使用它。

  有一些表的数据量很大,比如一张用户表,大约10亿条记录,50个字段,这种表,即使使用ORC压缩,单张表的存储也会超过100G,在HDFS使用双备份或者三备份的话就更大一些。

  表中的部分字段会被update更新操作,如用户联系方式,产品的描述信息,订单的状态等等。

  需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态。

  表中的记录变化的比例和频率不是很大,比如,总共有10亿的用户,每天新增和发生明显的变化的有200万左右,变化的比例占的很小。

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

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

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

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

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

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

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

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

  在2017-01-02这一天表中的数据是, 用户002和004资料做了修改,005是新增用户:

  在2017-01-03这一天表中的数据是, 用户004和005资料做了修改,006是新增用户:

  如果在数据仓库中设计成历史拉链表保存该表,则会有下面这样一张表,这是最新一天(即2017-01-03)的数据:

  t_start_date表示该条记录的生命周期开始时间,t_end_date表示该条记录的生命周期结束时间。

  在现在的大数据场景下,大部分的公司大部分会选择以Hdfs和Hive为主的数据仓库架构。目前的Hdfs版本来讲,其文件系统中的文件是不能做改变的,也就是说Hive的表智能进行删除和添加操作,而不可以进行update。基于这个前提,我们来实现拉链表。

  还是以上面的用户表为例,我们要实现用户的拉链表。在实现它之前,我们应该先确定一下我们有哪些数据源可以用。

  而且我们要确定拉链表的时间粒度,比如说拉链表每天只取一个状态,也就是说如果一天有3个状态变更,我们只取最后一个状态,这种天粒度的表其实已经能解决大部分的问题了。

  另外,补充一下每日的用户更新表该怎么获取,据笔者的经验,有3种方式拿到或者间接拿到每日的用户增量,因为它较为重要,所以详细说明:

  我们可以监听Mysql数据的变化,比如说用Canal,最后合并每日的变化,获取到最后的一个状态。

  假设我们天天都会获得一份切片数据,我们大家可以通过取两天切片数据的不同来作为每日更新表,这样的一种情况下我们大家可以对所有的字段先进行concat,再取md5,这样就ok了。

  然后我们还需要一张用户每日更新表,前面已经分析过该如果得到这张表,现在我们假设它已经存在。

  然后初始化的sql就不写了,其实就相当于是拿一天的ods层用户表过来就行,我们写一下每日的更新语句。

  现在我们假设我们已已经初始化了2017-01-01的日期,然后需要更新2017-01-02那一天的数据,我们有了下面的Sql。

  好了,我们分析了拉链表的原理、设计思路、并且在Hive环境下实现了一份拉链表,下面对拉链表做一些小的补充。

  流水表存放的是一个用户的变更记录,比如在一张流水表中,一天的数据中,会存放一个用户的每条修改记录,但是在拉链表中只有一条记录。

  这是拉链表设计时必须要格外注意的一个粒度问题。我们当然也可设为的粒度更小一些,一般按天就足够。

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

  在一些查询引擎中,我们对start_date和end_date做索引,这样能提高不少性能。

  保留部分历史数据,比如说我们一张表里面存放全量的拉链表数据,然后再对外暴露一张只提供近3个月数据的拉链表。

  使用拉链表的时候能不加t_end_date,即失效日期,但是加上之后,能优化很多查询。

  在拉链表的设计中可以加一些内容,因为我们每天保存一个状态,如果我们在这个状态里面加一个字段,比如如当天修改次数,那么拉链表的作用就会更大。