缓慢变化维解决方案之拉链表

  使用配置
内容纲要

概要描述


拉链表是一种数据模型,主要是针对数据仓库设计中表存储数据的方式而定义的;顾名思义,所谓拉链表,就是记录历史,记录一个事务从开始一直到当前状态的所有变化的信息。
拉链表可以避免按每一天存储所有记录造成的海量存储问题,同时也是处理缓慢变化数据(SCD2)的一种常见方式。
客户无需每天全量导入所有数据,只需要对变化的数据做增量导入,非常适用于ORC事务表。
相比于传统切片表,计算时大大减少了扫描记录,提高计算效率。

适用于:

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

若不使用拉链表,解决方案此处给出两种:

  • 方案一:每天只留最新的一份,比如我们每天抽取最新的一份全量数据到Inceptor中。(但无法查看历史数据)
  • 方案二:每天保留一份全量的切片数据。(存储空间占用量大,无法确定数据生命周期,保留多少份全量无法控制)

此时可以考虑使用拉链表

  • 首先它在空间上做了一个取舍,不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。
  • 其次它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。

详细说明


1. 构造历史拉链表

拉链表相较于截面表,多了两列,begindt和enddt,分别记录开始时间和结束时间

--历史拉链表(2016-11-01日数据)
> DROP TABLE account_hs;
> create table account_hs(
acid string,balance int,begindt date,enddt date
) 
clustered by(balance) into 1 buckets 
stored as ORC 
tblproperties("transactional"="true");
> insert into account_hs values('B',1287,'2016-11-01','2999-12-31');
> insert into account_hs values('C',8888,'2016-11-01','2999-12-31');
> insert into account_hs values('A',4439,'2016-11-01','2999-12-31');

file

--2016-11-02截面表
> DROP TABLE account_20161102;
> create table account_20161102(
acid string,balance int,curdt date
) 
clustered by(balance) 
into 1 buckets 
stored as ORC 
tblproperties("transactional"="true");
> insert into account_20161102 values('B',1287,'2016-11-02');
> insert into account_20161102 values('C',8888,'2016-11-02');
> insert into account_20161102 values('A',8234,'2016-11-02');
--2016-11-03截面表
> DROP TABLE account_20161103;
> create table account_20161103(
acid string,balance int,curdt date
) 
clustered by(balance) 
into 1 buckets 
stored as ORC 
tblproperties("transactional"="true");
> insert into account_20161103 values('B',1287,'2016-11-03');
> insert into account_20161103 values('A',8234,'2016-11-03');

首先我们分析下2016-11-02和2016-11-03这两天对数据做的操作:
2016-11-02日:对acid=A的数据做更改,所以需要做新增数据(step2)和封链(step3);
2016-11-03日:对acid=C的数据做删除,所以需要做封链删除(step4)。

2. 新增数据

--插入2016-11-02发生变动的数据
> insert into table account_hs
select
    acid, balance, begindt, enddt
FROM
    (select
         acid, balance, curdt as begindt, cast('2999-12-31' as date) as enddt
     from
         account_20161102
      except
      SELECT
          acid, balance, cast('2016-11-02' as date) as begindt, enddt
      FROM
          account_hs
      WHERE
           begindt <= '2016-11-01' AND enddt > '2016-11-01');

file

3. 封链

--将acid=A,balance=4439 这条数据的enddt做修改,有效日期截止2016-11-02
> MERGE INTO account_hs m
    USING (SELECT
                a.begindt as begindt, a.acid as acid
            FROM
                account_hs a
                INNER JOIN account_hs b
                ON b.begindt = cast('2016-11-02' as date)
                    AND a.acid = b.acid
            WHERE
                 a.begindt <= cast('2016-11-01' as date)
                 AND a.enddt > cast('2016-11-01' as date)) c
    ON(m.begindt = c.begindt and m.acid = c.acid)
WHEN MATCHED THEN
    UPDATE SET enddt = cast('2016-11-02' as date);

file

4. 删除数据的封链

--2016-11-03日acid=C的数据被删除,enddt日期修改为2016-11-03当天
> UPDATE account_hs a SET
    a.enddt = cast('2016-11-03' as date)
WHERE
     NOT EXISTS(SELECT
                    1
                FROM
                    account_20161103 b
                WHERE
                     a.acid = b.acid)
     AND a.begindt <= cast('2016-11-03' as date)
     AND a.enddt > cast('2016-11-03' as date);

file

5. 查询测试

1. 比如我想查询2016-11-02日的历史数据

> SELECT * FROM account_hs WHERE begindt <= cast('2016-11-02' as date) AND enddt > cast('2016-11-02' as date);

file

2. 比如我想查询最新的数据

> SELECT * FROM account_hs WHERE enddt = cast('2999-12-31' as date);

file

这篇文章对您有帮助吗?

平均评分 5 / 5. 次数: 1

尚无评价,您可以第一个评哦!

非常抱歉,这篇文章对您没有帮助.

烦请您告诉我们您的建议与意见,以便我们改进,谢谢您。