内容纲要
概要描述
拉链表是一种数据模型,主要是针对数据仓库设计中表存储数据的方式而定义的;顾名思义,所谓拉链表,就是记录历史,记录一个事务从开始一直到当前状态的所有变化的信息。
拉链表可以避免按每一天存储所有记录造成的海量存储问题,同时也是处理缓慢变化数据(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');
--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');
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);
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);
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);
2. 比如我想查询最新的数据
> SELECT * FROM account_hs WHERE enddt = cast('2999-12-31' as date);