数仓项目-拉链表技术精解

数仓项目-拉链表技术精解

一、拉链表的使用场景

在数据仓库的模型设计的过程中,通常我们会碰到那种非常大的业务基础信息表,如用户表;

假如一个用户表有10亿条记录,50个列,就算使用orc压缩,但张表的存储也会超过100G,如果同步到Hive中按HDFS的默认备份,那就是300G,这样对磁盘的消耗也是非常大的。

假设该表的某些字段在业务端会产生update操作,但是每次update的字段就那么1到2个,其它字段不变,那么这些变化不频繁的维度字段被称为缓慢渐变维,而且相同id的变update频率很小,每天update的记录只占全表记录的很小一部分,如1/20000。

这种场景的特点:

  1. 表中的部分字段会被update,例如:
    • 用户的手机号码,用户地址,生日信息等等;
  2. 需要查看某一个时间点或者时间段的历史快照信息,例如:
    • 查看某一个用户在历史某一时间点的手机号码
    • 查看某一个用户在过去某一段时间内,更新过几次等等
  3. 变化的比例和频率不是很大,例如:
    • 总共有1000万的用户,每天新增和发生变化的有10万左右

那么此时有3种同步方案:

  1. 每日全量覆盖的方式,特点:简单易操作,但是不支持保留历史数据;
  2. 每日全量切片的方式,特点:简单易操作,且能保证历史,但是会导致数仓存储大量的重复冗余数据,占用大量磁盘空间;
  3. 拉链表,特点:拉链表能保证每条记录的生命周期的start-date和end-date,既能保证历史数据,同时也优化了冗余存储。

二、一个简单的拉链表示例

假如业务那边有一个users表,表结构如下

2021-12-01的数据

用户id注册日期手机号码
0012021-12-0111111111
0022021-12-0122222222
0032021-12-0133333333

2021-12-02的数据

003 的电话号码作了修改,从 33333333 变成了 32323232 ,同时新增了一个用户 004

用户id注册日期手机号码
0012021-12-0111111111
0022021-12-0122222222
0032021-12-0132323232
0042021-12-0244444444

2020-07-03的数据

002 的电话号码作了修改,从 22222222 变成了 21212121 ,同时新增了一个用户 005

用户id注册日期手机号码
0012021-12-0111111111
0022021-12-0121212121
0032021-12-0132323232
0042021-12-0244444444
0052021-12-0355555555

那么假如我们按照T+1的方式同步数据,那么拉链表在hive中存储样式应该如下:

start_date 代表记录的生效起始时间,end_date 代表记录失效时间

假如我们需要查询 002cal_date 的对应的有效记录,可以使用 start_date <= cal_date and end_date > cal_date 进行限定。

如果我们需要查询当前的有效数据,那么我们只需要按照 where end_date = '9999-12-31' 来进行限定就okay了。

最后,我们希望得到的数据:

用户id注册日期手机号码start_dateend_date
0012021-12-01111111112021-12-019999-12-31
0022021-12-01222222222021-12-012021-12-03
0032021-12-01333333332021-12-012021-12-02
0032021-12-01323232322021-12-029999-12-31
0042021-12-02444444442021-12-029999-12-31
0022021-12-01212121212021-12-039999-12-03
0052021-12-03555555552021-12-039999-12-31

三、拉链表存储历史快照代码实现方案

操作步骤:

  1. 在原有dw层表上,添加额外的两列

    • 生效日期(start_date)
    • 失效日期(end_date)
  2. 只同步当天修改的数据到ods层

  3. 拉链表算法实现

    • 编写SQL处理dw层历史数据,重新计算之前的dw_end_date

    • 编写SQL处理当天最新的数据(新添加的数据和修改过的数据)

  4. 拉链表的数据为:当天历史数据 UNION ALL 最新的数据

四、在Hive中实现拉链表

⚠️注意:在向大数据转型的趋势下,很多企业将Hive作为数据仓库的首选,但是Hive数据是基于HDFS的文件,只支持delete和insert操作,不支持update

  • users
  • users_inc
  • users_his

整体思路:dw的新数据 = dw的旧数据+新增数据

1、数据准备

create database ods;
create database dw;
-- ods的原始切片表
CREATE TABLE `ods`.`users` (
reg_date STRING COMMENT '注册日期',
  user_id STRING COMMENT '用户编号',
  mobile 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的更新表,用来存储新增记录
CREATE TABLE `ods`.`users_inc` (
  reg_date STRING COMMENT '注册日期',
  user_id STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码'
)
COMMENT '每日用户资料更新表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC;
--LOCATION '/ods/user_inc';


--dw的拉链表
CREATE TABLE `dw`.`users_his` (
  reg_date STRING COMMENT '用户编号',
  user_id STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  start_date STRING,
  end_date STRING
)
COMMENT '用户资料拉链表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC;
--LOCATION '/dw/user_his';

insert into `ods`.`users` PARTITION (dt = '2021-12-01') values('2021-12-01',001,'11111111'),('2021-12-01',002,'22222222'),('2021-12-01',003,'33333333');

2、全量更新,users_his 表初始化

首先全量更新,我们先到 2021-12-01 为止的数据。

初始化,先把 2021-12-01 的数据初始化进去

INSERT overwrite TABLE `ods`.`users_inc` PARTITION (dt = '2021-12-01')
SELECT reg_date,user_id,mobile
FROM `ods`.`users`
WHERE reg_date < '2021-12-02' and reg_date <'2021-12-02';

刷到 dw

INSERT overwrite TABLE `dw`.`users_his`
SELECT reg_date,user_id,mobile,
reg_date AS start_date,
'9999-12-31' AS end_date
FROM `ods`.`users_inc`
WHERE dt = '2021-12-01';

dw.users_his 中的数据如下:

2021-12-01      001     11111111        2021-12-01      9999-12-31
2021-12-01      002     22222222        2021-12-01      9999-12-31
2021-12-01      003     33333333        2021-12-01      9999-12-31

3、剩余需要进行增量更新

--003的电话号码作了修改,从33333333变成了32323232,同时新增了一个用户004
truncate table `ods`.`users_inc`;
insert into `ods`.`users_inc` PARTITION (dt = '2021-12-02') values('2021-12-01',003,'32323232'),('2021-12-02',004,'44444444');

users_inc 中的增量数据

2021-12-02      002     21212121        2021-12-02
2021-12-02      003     32323232        2021-12-02
2021-12-02      004     44444444        2021-12-02

先放到增量表中,然后进行关联到一张临时表中,在插入到新表中

--此步,可以先创建一个users_his_tmp,然后再同步到users_his。为了方便直接同步。
--DROP TABLE IF EXISTS `dw`.`users_his_tmp`;
--CREATE TABLE `dw`.`users_his_tmp` AS 拉链表代码
--INSERT overwrite TABLE `dw`.`users_his` SELECT *  FROM `dw`.`users_his_tmp`;
INSERT overwrite TABLE `dw`.`users_his`
SELECT 
 * 
FROM(
 --将修改的数据进行生命周期的修改,
 SELECT
  a.reg_date,
  a.user_id,
  a.mobile,
  a.start_date,
  case 
   when a.end_date = '9999-12-31' and b.user_id is not null then '2021-12-02'
   else a.end_date
   end as end_date  --说明通过user_id关联上了,在user_inc表中也算今日的新增数据
 FROM `dw`.`users_his`  a
 LEFT JOIN `ods`.`users_inc` b
 ON a.user_id = b.user_id
 UNION 
  --将新增的数据直接union进来
 SELECT 
  reg_date,
  user_id,
  mobile,
  '2021-12-02' as start_date,
  '9999-12-31' as end_date
 FROM `ods`.`users_inc`
) x;


dw.users_his 中的数据如下:

2021-12-01      001     11111111        2021-12-01      9999-12-31
2021-12-01      002     22222222        2021-12-01      9999-12-31
2021-12-01      003     33333333        2021-12-01      2021-12-02
2021-12-02      003     32323232        2021-12-02      9999-12-31
2021-12-02      004     44444444        2021-12-02      9999-12-31

4、按照上面步骤3,把 2021-12-03 号的数据更新进去,最后结果如下

--002的电话号码作了修改,从22222222变成了21212121,同时新增了一个用户005
truncate table `ods`.`users_inc`;
insert into `ods`.`users_inc` PARTITION (dt = '2021-12-03') values('2021-12-01',002,'21212121'),('2021-12-03',005,'55555555');

INSERT overwrite TABLE `dw`.`users_his`
SELECT 
 * 
FROM(
 --将修改的数据进行生命周期的修改,
 SELECT
  a.reg_date,
  a.user_id,
  a.mobile,
  a.start_date,
  case 
   when a.end_date = '9999-12-31' and b.user_id is not null then '2021-12-03'
   else a.end_date
   end as end_date  --说明通过user_id关联上了,在user_inc表中也算今日的新增数据
 FROM `dw`.`users_his`  a
 LEFT JOIN `ods`.`users_inc` b
 ON a.user_id = b.user_id
 UNION 
  --将新增的数据直接union进来
 SELECT 
  reg_date,
  user_id,
  mobile,
  '2021-12-03' as start_date,
  '9999-12-31' as end_date
 FROM `ods`.`users_inc`
) x;

dw.users_his 中的数据如下:

2021-12-01      001     11111111        2021-12-01      9999-12-31
2021-12-01      002     21212121        2021-12-03      9999-12-31
2021-12-01      002     22222222        2021-12-01      2021-12-03
2021-12-01      003     32323232        2021-12-02      9999-12-31
2021-12-01      003     33333333        2021-12-01      2021-12-02
2021-12-02      004     44444444        2021-12-02      9999-12-31
2021-12-03      005     55555555        2021-12-03      9999-12-31

查询当前所有有效的记录

select * from users_his where end_date='9999-12-31';

查询 2021-12-01 的历史快照

select * from users_his where start_date <= '2021-12-01' and end_date >= '2021-12-01';

五、用MySQL来模拟hive的拉链表

由于MySQL中不支持Hive中的 insert overwrite ,所以用一张临时表进行代替,SQL如下:

create table if not exists user(reg_date  varchar(24),user_id int ,mobile varchar(24))  engine = innodb  default charset = utf8mb4;

create table if not exists user_update(reg_date  varchar(24),user_id int ,mobile varchar(24))  engine = innodb  default charset = utf8mb4;

create table if not exists user_his(reg_date  varchar(24),user_id int ,mobile varchar(24),start_date varchar(24) ,end_date varchar(24))  engine = innodb  default charset = utf8mb4;


######################2021-12-01###################

truncate table user;
truncate table user_update;
truncate table user_his;
insert into user values('2021-12-01',001,'11111111'),('2021-12-01',002,'22222222'),('2021-12-01',003,'33333333');
insert into user_update select * from user;

truncate table user_his;
insert into user_his
select 
*
from (
	select 
		a.reg_date,
		a.user_id,
		a.mobile,	
		a.start_date as start_date,
		if(b.user_id is not null and a.end_date ='9999-12-31','2021-12-01',a.end_date)
	from user_his a
	left join user_update b
	on a.user_id = b.user_id
	union 
	select 
		reg_date,
		user_id,
		mobile,
		'2021-12-01' as start_date,
		'9999-12-31' as end_date  
	from user_update
) tmp ;


######################2021-12-02###################
#模拟新增、修改数据,在新增表中,修改与新增都属于新增

delete from user_update where user_id= 001;
update  user_update set mobile = '21212121' where user_id = 002;
update  user_update set mobile = '32323232' where user_id = 003;
insert into user_update values('2021-12-02',004,'44444444');

drop table if exists user_his_temp;
create table if not exists user_his_temp as select * from user_his;

truncate table user_his;
insert into user_his
select 
	*
from (
	select 
		a.reg_date,
		a.user_id,
		a.mobile,	
		a.start_date as start_date,
		if(b.user_id is not null and a.end_date ='9999-12-31','2021-12-02',a.end_date)
	from user_his_temp a
	left join user_update b
	on a.user_id = b.user_id
	union 
	select 
		reg_date,
		user_id,
		mobile,
		'2021-12-02' as start_date,
		'9999-12-31' as end_date  
	from user_update
) tmp ;


参考

hive中拉链表

解决缓慢变化维—拉链表

数据仓库中的拉链表(hive实现)

Hive中如何正确的使用拉链表

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《DRAM技术》是一本关于动态随机存取存储器(DRAM)技术的重要参考书。本书是第二版,经过深入研究和实践,对第一版进行了补充和更新。 书中首先介绍了DRAM的基本原理和结构。DRAM是一种常见的计算机主存储器,采用电容来存储数据,并借助电容电荷的存放和释放来读写数据。本书详细释了DRAM的内部结构,包括位线、单元电容、行选择线和列选择线等重要组成部分。 其次,本书对DRAM的读写过程进行了深入的分析和释。涉及了DRAM读写加速技术、预取缓存、行缓存和列缓存等内容。读者可以通过学习这些内容,更好地理和应用DRAM技术。 本书还介绍了DRAM的性能和可靠性优化技术。讲了如何通过改进DRAM的访问速度、容量和稳定性来提高系统的性能和可靠性。同时,还对DRAM的错误检测和纠正技术进行了详细阐述,以提高系统的数据完整性和可靠性。 此外,本书还包括了面向DRAM技术发展的新挑战和前景的讨论。讲述了DRAM容量扩大、功耗优化、高速传输和可扩展性等领域的最新研究成果和未来趋势。 总而言之,《DRAM技术》第二版是一本全面系统地介绍和析DRAM技术的重要参考书。读者通过学习本书可以深入了DRAM的内部结构、读写过程、性能优化和可靠性技术,对DRAM技术有更深入的认识,并为应对未来的技术挑战提供有益的参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yiluohan0307

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值