Code前端首页关于Code前端联系我们

数据仓库拉链表(原理、Hive中的设计与实现)

terry 2年前 (2023-09-26) 阅读数 44 #数据库
  1. 首先说说拉链表的用途,拉链表是什么。
  2. 让我们通过一些小的使用场景以及拉链表和常用切片表的区别来了解更多关于拉链表的知识。
  3. 坚持针对具体的应用场景来设计并实现一个拉链表,最后用一些例子来说明我们设计的表的使用(因为现在Hive的大量使用,我们使用Hive的场景拿以下面的型号为例)。
  4. 分析拉链式桌子的优缺点,并对前面提到的一些内容进行进一步解释,例如拉链式桌子和自来水之间的关系。

0x01 什么是拉链表

拉链表是为表在数据仓库设计中存储数据的方式而定义的。顾名思义,所谓拉链的目的就是记录历史。记录有关对象从开始到当前状态的所有更改的信息。

我们先来看一个例子。这是一个zip表,存储了基本的用户信息和每条记录的生命周期。这个表可以让我们获取当天的最新数据以及之前的历史数据。 † 01-01

9999-12-312017-01-010022222222017-01-012017-01-012017-01-01 2017-01-01 。 2017 - 01-01-012017-01-019999-12-312017-01-010044444442017-01-011.1 .2017- 010044324322017 -01-022017-01-022017-01-010044324322017-01-032017。 2017-01-020055555552017-01-022017-01-022017-01-020051151152017-01-032017-01-03 2017年01期-02 ‹ 17-01-03006666666 2017-01-039999-12-31

现在,我们不会详细解释此表。下面文章具体讲解如何设计、实现和使用。

拉链表使用场景

在数据库数据模型设计过程中,我们经常会遇到以下表设计:

  1. 有些表包含大量数据,例如用户表。大约有10亿条记录,50个字段,这样的表即使使用ORC压缩,单表的存储空间也超过100GB。如果HDFS使用双备份或者三备份的话,会比较大。
  2. 表中部分字段通过更新功能进行更新,如用户联系信息、产品描述信息、订单状态等。
  3. 您需要查看特定时间或周期的历史快照数据。例如,查看订单在历史上某个时刻的状态。
  4. 表记录变化的比例和频率都不是很大。例如,总共有10亿用户,每天大约有200万的新增和变化。变化的份额非常小。

这样的桌子应该如何设计呢?以下是几个选项:

  • 选项 1:每天仅保留最新副本。例如,我们每天使用Sqoop将最新的全量数据提取到Hive中。
  • 选项 2:每天保留零件数据的完整副本。
  • 选项 3:使用带拉链的桌子。

为什么要使用拉链表

现在我们就对上面提到的三个一一进行分析。

计划1

不用说,这个计划实施起来非常简单。每天删除前一天的数据并再次提取最新的数据。

优点很明显,节省空间,对于一些常用用途也很方便。选择表时无需添加时间段。

缺点也很明显。没有历史数据。首先检查旧帐户的唯一方法是使用其他方法,例如从流程图中绘制。

方案2

每天吃足量的片是比较安全的方案,而且历史数据也有。

缺点是预留存储空间太大。如果每天都保留这张表的完整副本,则每个完整副本都会存储大量不可变数据,这是存储的巨大浪费。我还是很深刻...

当然我们也可以做一些妥协比如只保留上个月的数据?然而,需求是无耻的,我们无法完全掌控数据的生命周期。

拉链链手表

拉链链手表基本上考虑到了我们在使用中的需求。

首先,它会牺牲空间。虽然占用空间没有方案一那么小,但每天的涨幅也只能是方案二的千分之一甚至万分之一。

事实上,它可以满足选项2可以满足的需求。不仅可以获取最新数据,还可以添加过滤条件,获取历史数据。

所以我们还是需要使用拉链桌。

0x02 拉链列表的设计与实现

如何设计拉链列表

我们举个栗子,详细看一下拉链列表。

我们继续第《漫谈数据仓库之维度建模》 部分中的电子商务网站示例,现在使用用户的拉链列表进行说明。

我们先看一下Mysql关系型数据库user表的数据变化。

表格中2017年1月1日的信息为:

注册日期 用户号码 手机号码 手机号码 ❀10101017- 7-01- 01002222222
2017-01-01003333333
2017-01-01004444444

2017-01-02 排班数据为用户 002 和 004 信息已修改,005 为新用户:

注册日期用户号码手机号码备注
2017-01-012.2.201721. 233333)
2017-01 -01003333333
2017-01-01004432432(从 444444 更改为 432432)
2017-01-02005555555 ♹(2017-01-01新增)02017)日程2017-01-03 据了解,用户004、005的信息已编辑,006为新用户:
注册日期用户号码手机号码7100 s -01 001111111
2017 -01-01002233333
2017-01-01003333333
2017-01-01004654321(从 432432 - 654321)27❙←010 (55 5555 - 115115)
2017 -01-03006666666 (已添加 03.1.2017 )

如果该表设计为在数据仓库中保存为历史 zip 表,则会出现下表,这是最新日期(例如 2017-01 -03) 信息:

注册日期用户号码手机号码t_开始日期10011111112017-01-019999-12-31 2017-01- 010022222222017-01-012017-01-01
61017-01-7 02 9999 -12-31-31
2017-01-010033333332017 -01-019999-12-31
2017-01-012017-01-019999-12-31 01
2017-01-01004432432 2017-01- 022017-01-02
2017-01-01❀0179999-12-31.
2017-01-01 02005555555201.01.20170066666662017-01-039999-12-31

解释t_start_date代表记录的生命周期的开始时间,t_end_date =记录的生命周期的结束时间-112-- 31'表示该记录当前在有效的空间中。

  • 如果搜索所有有效记录,请选择 * from user where t_end_date = '9999-12-31'。
  • 如果查询2017-01-02历史快照,请选择* from user where t_start date = '2017-01-02'。 (这个一定要仔细理解,它是拉链表中比较重要的一部分。
  • 拉链表在Hive中的实现

    当前大数据场景中基于HDFS和Hive的数据仓库架构。在当前版本的HDFS中,文件系统中的文件是不可变的,这意味着Hive表可以智能删除和添加,但不能更新。基于这个出发点,实现了拉链列表。

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

    1. 我们需要一份完整的ODS层用户列表。至少应该用于初始化。
    2. 每日用户更新表。

    并且我们需要确定拉链表的时间精度。比如拉链桌每天就只有一个空间。换句话说,如果每天有 3 个状态变化,我们只取最后一个状态。这样的日常准确度表其实可以解决大部分问题。

    另外,我想补充一下如何获取每日用户更新表。根据笔者的经验,有3种方式可以获得或间接获得每日用户津贴。既然比较重要,我就详细解释一下:

    1. 我们可以使用例如Canal来跟踪Mysql数据的变化,最后结合每天的变化得到最后的状态。
    2. 假设我们每天都会收到切片数据。我们可以将两天的分片数据的差值作为每日更新表。这种情况我们可以先将所有字段组合起来,然后取md5就可以了。
    3. 流量计!每日更换时间表有效。

    Ods层用户表

    现在我们看一下ods层用户数据分区表的结构:

    CREATE EXTERNAL TABLE ods.user (
      user_num STRING COMMENT '用户编号',
      mobile STRING COMMENT '手机号码',
      reg_date STRING COMMENT '注册日期'
    COMMENT '用户资料表'
    PARTITIONED BY (dt string)
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
    STORED AS ORC
    LOCATION '/ods/user';
    )
    

    Ods层user_update表我们还需要用户每日更新表,我们之前已经分析过如何获取这张表,现在我们假设它已经存在。

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

    拉链表

    现在我们创建一个拉链表:

    CREATE EXTERNAL TABLE dws.user_his (
      user_num STRING COMMENT '用户编号',
      mobile STRING COMMENT '手机号码',
      reg_date STRING COMMENT '用户编号',
      t_start_date ,
      t_end_date
    COMMENT '用户资料拉链表'
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
    STORED AS ORC
    LOCATION '/dws/user_his';
    )
    

    执行sql命令

    那么初始化sql就不写了。其实相当于用了ods级别的用户表一天。我们在这里写每日更新。

    现在假设我们已经初始化了日期2017-01-01,那么数据需要更新到2017-01-02。我们有以下 SQL。

    然后仅设置两个日期作为变量。

    INSERT OVERWRITE TABLE dws.user_his
    SELECT * FROM
    (
        SELECT A.user_num,
               A.mobile,
               A.reg_date,
               A.t_start_time,
               CASE
                    WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'
                    ELSE A.t_end_time
               END AS t_end_time
        FROM dws.user_his AS A
        LEFT JOIN ods.user_update AS B
        ON A.user_num = B.user_num
    UNION
        SELECT C.user_num,
               C.mobile,
               C.reg_date,
               '2017-01-02' AS t_start_time,
               '9999-12-31' AS t_end_time
        FROM ods.user_update AS C
    ) AS T
    

    0x03补充

    好了,我们分析了拉链表的原理和设计思路,并在Hive环境下实现了拉链表。让我们对拉链表进行一些小的添加。

    Zip 表和流表

    流表存储用户变更记录。例如,在流表中,一天的数据存储了每个用户的更改记录,但在拉链表中,只有一条记录。

    这是设计拉链表时需要解决的精度问题。当然我们也可以把分辨率设置低一些,一般每天就够了。

    查询性能

    当然,zip表也面临查询性能问题。比如我们保存zip数据5年,这张表必然会比较大,查询时性能会比较低。个人认为有两个思路可以解决:

    1. 在一些查询引擎中,我们对开始日期和结束日期建立索引,这样可以大大提高性能。
    2. 保留一些历史信息。例如,我们将整个 zip 表的数据存储在一个表中,然后公开一个仅包含最近 3 个月数据的 zip 表。

    0xFF 总结

    我们在本文中详细分享了与拉链列表相关的信息点,但仍然缺少一些内容。欢迎交流。

    从后来的使用中积累了经验,所以我补充一下:

    1. 在使用zipper表时,不需要添加t_end_date,即最后一个有效期,但是添加后可以优化很多查询。
    2. 您可以添加当前线路状态标记,以便快速查找当前线路状态。
    3. 您可以在拉链桌设计中添加内容,因为我们每天都节省空间。如果我们在这个空间添加一个字段,比如每天的变化次数,拉链表会更有用。

    版权声明

    本文仅代表作者观点,不代表Code前端网立场。
    本文系作者Code前端网发表,如需转载,请注明页面地址。

    发表评论:

    ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

    热门