如何基于MRS-Hudi实现拉链表
【摘要】 拉链表是缓慢变化维的一种实现形式,是数据仓库的常用数据模型。基于传统大数据需要全量覆盖写的模式进行,基于Hudi的能力,实现方案会更加简单,性能更优。
拉链表介绍
数据仓库中有一种数据存储模型,用于维护历史状态以及最新状态,反映某一Key的历史变化演进过程,该种模型叫做缓慢变化维,简称SCD(Slowly Changing Dimensions),通常所说的拉链表其实属于缓慢变化维的一种实现方式,拉链表通过增加一列维度信息维护拉链状态,他们之间稍微有所不同,具体区别如下:
相同点:
缓慢变化维度,简称SCD (Slowly Changing Dimensions)和拉链表都是为了存储非静态数据的历史变化状态。
不同点:
拉链表有开链和闭链过程,总一条数据明确表示最新状态(例如常见的End_date=9999-12-31 00:00:00),针对的大都是事实表。
缓慢变化维,没有开链与闭链过程,只是单纯的记录数据的变化过程,解决的也都是维度表的存储,反映维度的数据历史演进过程。
具体举例如下:
- 缓慢变化维
缓慢变化维的变化相对拉链表要简单很多,直接插入最新状态即可,例如A02套餐内容中通话时长更新为2000分钟后,变化后的结果如下:
- 拉链表
在实现上增加了时效时间字段,用于表示保存变化后的最新状态值
具体实现如下:例如用户U0001更新了新套餐A02,操作如下
- 将当前最新状态数据更新,也就是闭链操作
变更为:
- 将最新状态数据插入,也就是开链操作
- 最终结果为:
基于Hudi表实现拉链表的方案
- 由于Hudi表存储为了保证数据唯一性要求有主键,拉链表会对某一个对象的历史状态都存储所以主键设计为联合主键,将对象ID和生效时间作为联合主键。
- Hudi新增了数据的更新能力,因此相对于传统的大数据平台,可以基于update的能力优化传统hive的拉链表的实现方案。
- 增量数据一般不携带历史数据的生效时间
拉链表实现算法
- 当前最新的拉链表为Now_table(UserID:用户ID,BundleID为套餐ID;Start_date为生效时间;End_date为失效效时间)
- 新增数据Tmp_table,(由于大数据平台的数据基于上游采集而来,基于时间戳的增量数据相对容易获取到)
- 新增数据Tmp_data与Now_table关联,将以存在数据更新写入Now_table,end_date为当前时间
- 将新增数据全部写入Now_table,end_date为‘9999-12-31’
实现举例
- 创建Now_table,并初始化数据
Create table Now_table(
userid string,
bundleid string,
start_date string,
end_date string,
ts timestamp
)using hudi
OPTIONS(
type = 'mor',
`payloadClass` 'org.apache.hudi.common.model.DefaultHoodieRecordPayload',
primaryKey = 'userid,start_date',
preCombineField = 'ts'
);
insert into Now_table(userid,bundleid,start_date,end_date) values('U0001','A01','2020-05-31','2021-05-31',now());
insert into Now_table(userid,bundleid,start_date,end_date) values('U0001','A01','2021-05-31','9999-12-31',now());
insert into Now_table(userid,bundleid,start_date,end_date) values('U0002','A01','2020-05-31','2021-05-31',now());
insert into Now_table(userid,bundleid,start_date,end_date) values('U0002','A02','2021-05-31','9999-12-31',now());
- 新增数据临时表Tmp_table,新增数据到Tmp_table
Create table Tmp_table(
userid string,
bundleid string,
start_date string
)using hudi
OPTIONS(
type = 'mor',
primaryKey = 'userid',
preCombineField = 'start_date'
);
insert into Tmp_table(userid,bundleid,start_date) values('U0001','A03','2022-05-31');
insert into Tmp_table(userid,bundleid,start_date) values('U0002','A03','2022-05-31');
insert into Tmp_table(userid,bundleid,start_date) values('U0003','A03','2022-05-31');
Tmp_table数据内容如下:
- 将Now_table数据闭链
insert into Now_table select t1.userid,t1.bundleid,t1.start_date,t2.start_date,now() from Now_table t1,Tmp_table t2 where t1.userid= t2.userid and t1.end_date='9999-12-31';
Now_table数据变更如下:
- 将增量数据开链写入Now_table
insert into Now_table select userid,bundleid,start_date,'9999-12-31',now() from Tmp_table
Now_table数据更新如下:
- 通过userid可以查询到历史的状态变化。
select userid,bundleid,start_date,end_date from Now_table where userid='U0001';
算法总结
- Hudi表具有数据更新能力,不需要对全表数据进行insert overwrite操作,算法更简单
- Hudi表提供Upsert的能力,当相同主键的数据存在,新数据会自动更新老数据,因此不用对老数据进行update操作,直接Insert即可(Sparksql的Insert操作默认为upsert操作。)
- 对于缓慢变化维的操作会更加简化,设置合理的主键,基于Merge语法直接操作,会更加简单。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)