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

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

1 拉链表的使用场景

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

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

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

1、 每日全量覆盖的方式

2、每日全量切片的方式

3、拉链表

方式1:简单易操作,但是不支持保留历史数据

方式2:简单易操作,且能保证历史,但是会导致数仓存储大量的重复冗余数据,占用大量磁盘空间

方式3:拉链表能保证每条记录的生命周期的start-date和end-date,既能保证历史数据,同时也优化了冗余存储;

2 拉链表特点

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

2020-07-01的数据

注册日期 用户id 手机号码
2020-07-01 001 111111
2020-07-01 002 222222
2020-07-01 003 333333

2020-07-02的数据

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

注册日期 用户id 手机号码
2020-07-01 001 111111
2020-07-01 002 222222
2020-07-01 003 323232
2020-07-02 004 444444

2020-07-03的数据

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

注册日期 用户id 手机号码
2020-07-01 001 111111
2020-07-01 002 212121
2020-07-01 003 323232
2020-07-02 004 444444
2020-07-03 005 555555

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

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

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

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

注册日期 用户id 手机号码 start_date end_date
2020-07-01 001 111111 2020-07-01 9999-12-31
2020-07-01 002 222222 2020-07-01 2020-07-03
2020-07-01 002 212121 2020-07-03 9999-12-31
2020-07-01 003 333333 2020-07-01 2020-07-02
2020-07-01 003 323232 2020-07-02 9999-12-31
2020-07-02 004 444444 2020-07-02 9999-12-31
2020-07-03 005 555555 2020-07-03 9999-12-31

3 在Hive中实现拉链表

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

  • user
  • user_update
  • user_zip

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

-- ods的原始切片表
create table if not exists ods.user(reg_date date,user_id string,mobile string) 
comment '用户信息表'
row format delimited fields terminated by '\t' lines terminated by '\n';

--ods的更新表,用来存储新增记录
--怎么定义新增?将所有字段concat、然后取md5值与ods的原始切片表进行比较
create table if not exists ods.user_update(reg_date date,user_id string,mobile string) 
comment '用户信息新增表'
row format delimited fields terminated by '\t' lines terminated by '\n';

--dw的拉链表
create table if not exists dw.user_zip(reg_date date,user_id string,mobile string,start_date string ,end_date string) 
comment '用户信息拉链表'
row format delimited fields terminated by '\t' lines terminated by '\n';

DW层同步代码为:

INSERT OVERWRITE TABLE user_zip
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 '2020-07-02'
  		else a.end_date
  	end as end_date  --说明通过user_id关联上了,在user_update表中也算今日的新增数据
  FROM dw.user_zip  a
  LEFT JOIN ods.user_update b
  ON a.user_id = b.user_id
  
  UNION 
  --将新增的数据直接union进来
  SELECT 
  	reg_date,
    user_id,
    mobile,
  	'2020-07-02' as start_date,
  	'9999-12-31' as end_date
  FROM ods.user_update
 
) fi_table


--最终将dw的数据每日全量覆盖

4 用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;


######################2020-07-01###################

truncate table user;
truncate table user_update;
truncate table user_his;
insert into user values('2020-07-01',001,'111111'),('2020-07-01',002,'222222'),('2020-07-01',003,'333333');
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','2020-07-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,
		'2020-07-01' as start_date,
		'9999-12-31' as end_date  
	from user_update
) tmp ;


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

delete from user_update where user_id= 001;
update  user_update set mobile = '212121' where user_id = 002;
update  user_update set mobile = '323232' where user_id = 003;
insert into user_update values('2020-07-02',004,'444444');

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','2020-07-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,
		'2020-07-02' as start_date,
		'9999-12-31' as end_date  
	from user_update
) tmp ;

你可能感兴趣的:(Hive,hive)