面试官问:你了解SCD吗?一般会用什么方式来解决SCD的问题?
回答思路:
- 首先介绍一下什么是SCD
- 什么业务场景下遇到过SCD的问题
- 用什么方案解决的,还有其他的那些解决思路
什么是SCD(缓慢变化维)
SCD:Slowly Changing Dimension,缓慢变化维就是变化相对缓慢(相对于变化快速的事实数据来说)的维度。在现实世界中,维度的属性并不是静态的,它会随着时间流逝发生缓慢的变化,这种随着时间发生变化的维度我们一般称之为“缓慢变化维”,并且把处理维度表的历史变化信息的问题称之为处理缓慢变化维的问题,有时也简称为“处理SCD的问题” 。
什么业务场景下会遇到缓慢变化维的问题
- 用户表(亿级别),每天新增用户是万级别,每天全量保存数据太大,增量保存又不方便查询;
- 表中的部分字段会被update更新操作。员工部门表,有一些员工内部活水(调岗)导致部门信息变更,对应绩效计算的时候会有工作时间缺失;
- 需要查看某一时间点或者时间段的历史快照信息,比如查看某一个订单的历史某一个时间点的状态;
- 表的记录变化占比不是很大,比如有10亿的用户,每天变化的有几十万到百万,变化的占比很小。
缓慢变化维的解决方案
根据业务具体的需求,一般有三种方案解决缓慢变化维的问题:
- 方案一:每天只保留最新一份数据:直接覆盖、保留最新值
- 方案二:每天保留一份全量的切片数据
- 方案三:使用拉链表
- 增加一列,保留初始状态跟最新状态(或者最近两次的状态)
- 增加行,记录每行的状态变化时间点跟状态值,记录每个主体的全生命状态流程
方案一
这种方案实现起来很简单,每天删除前一天的数据,重新抽取一份最新的。
优点:节约了空间,查询时不用考虑分区问题;
缺点:没有历史数据,历史回溯分析困难。
方案二
每天一份全量的切片时一种比较稳妥的方案,而且历史数据也在。
缺点:占用存储空间太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费,可以考虑存储周期TTL设置。
拉链表
拉链表基本上兼顾了我们的需求。
首先在空间上,不会像每天全量备份一样占用太多的空间,每日增量可能只有总表的千分之一二;既能获取最新的数据,也能添加筛选条件获取历史数据。
什么是拉链表
拉链表是针对数据仓库中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。
从上面的示例看,这就是一张拉链表,存储的是用户的最基本的信息以及每条记录的声明周期。我们可以使用这张拿到最新的当天的数据以及之前的历史数据。
注册日期 | 用户编号 | 手机号码 |
---|---|---|
2024-01-01 | 001 | 13611111111 |
2024-01-01 | 002 | 13622222222 |
2024-01-01 | 003 | 13633333333 |
2024-01-01 | 004 | 13644444444 |
在2024-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:
2024-01-02这天的拉链表数据应该如下
- 生效时间:表示该条记录的生命周期开始时间
- 失效时间:表示该条记录的生命周期的结束时间,9999-12-31表示该条记录目前有效
-
--如果查询当前所有有效的记录 select * from table_user where `失效时间` = '9999-12-31' --如果查找2024-01-02当天有效的数据 select * from table_user where `失效时间` >= '2024-01-02' and `生效时间` <= '2024-01-02'
拉链表数据更新
-
INSERT OVERWRITE TABLE dws.user_his SELECT * FROM ( SELECT A.user_num, A.mobile, A.reg_date, A.t_start_time, CASE WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01' ELSE A.t_end_time END AS t_end_time FROM dws.user_his AS A LEFT JOIN ods.user_update AS B ON A.user_num = B.user_num --将有变更的记录失效时间更新 UNION SELECT C.user_num, C.mobile, C.reg_date, '2017-01-02' AS t_start_time, '9999-12-31' AS t_end_time FROM ods.user_update AS C ) AS T --更新的数据:新增一条失效时间为9999-12-31的新的记录