缓慢变化维【拉链表】

本文讨论了SCD(SlowlyChangingDimension)的概念,指出在大量用户数据更新频繁的场景中如何处理。介绍了三种解决SCD问题的方案:删除旧数据的简单覆盖法、保留全量切片的稳妥方案,以及利用拉链列表兼顾历史数据和空间效率的方法。
摘要由CSDN通过智能技术生成

面试官问:你了解SCD吗?一般会用什么方式来解决SCD的问题?

回答思路:

  1. 首先介绍一下什么是SCD
  2. 什么业务场景下遇到过SCD的问题
  3. 用什么方案解决的,还有其他的那些解决思路

什么是SCD(缓慢变化维)

SCD:Slowly Changing Dimension,缓慢变化维就是变化相对缓慢(相对于变化快速的事实数据来说)的维度。在现实世界中,维度的属性并不是静态的,它会随着时间流逝发生缓慢的变化,这种随着时间发生变化的维度我们一般称之为“缓慢变化维”,并且把处理维度表的历史变化信息的问题称之为处理缓慢变化维的问题,有时也简称为“处理SCD的问题” 。

什么业务场景下会遇到缓慢变化维的问题

  • 用户表(亿级别),每天新增用户是万级别,每天全量保存数据太大,增量保存又不方便查询;
  • 表中的部分字段会被update更新操作。员工部门表,有一些员工内部活水(调岗)导致部门信息变更,对应绩效计算的时候会有工作时间缺失;
  • 需要查看某一时间点或者时间段的历史快照信息,比如查看某一个订单的历史某一个时间点的状态;
  • 表的记录变化占比不是很大,比如有10亿的用户,每天变化的有几十万到百万,变化的占比很小。

缓慢变化维的解决方案

根据业务具体的需求,一般有三种方案解决缓慢变化维的问题:

  1. 方案一:每天只保留最新一份数据:直接覆盖、保留最新值
  2. 方案二:每天保留一份全量的切片数据
  3. 方案三:使用拉链表
    1. 增加一列,保留初始状态跟最新状态(或者最近两次的状态)
    2. 增加行,记录每行的状态变化时间点跟状态值,记录每个主体的全生命状态流程

 方案一

这种方案实现起来很简单,每天删除前一天的数据,重新抽取一份最新的。

优点:节约了空间,查询时不用考虑分区问题;

缺点:没有历史数据,历史回溯分析困难。

方案二

每天一份全量的切片时一种比较稳妥的方案,而且历史数据也在。

缺点:占用存储空间太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费,可以考虑存储周期TTL设置。

拉链表

拉链表基本上兼顾了我们的需求。

首先在空间上,不会像每天全量备份一样占用太多的空间,每日增量可能只有总表的千分之一二;既能获取最新的数据,也能添加筛选条件获取历史数据。

 什么是拉链表

拉链表是针对数据仓库中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。

 从上面的示例看,这就是一张拉链表,存储的是用户的最基本的信息以及每条记录的声明周期。我们可以使用这张拿到最新的当天的数据以及之前的历史数据。

注册日期用户编号手机号码
2024-01-0100113611111111
2024-01-0100213622222222
2024-01-0100313633333333
2024-01-0100413644444444

在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的新的记录
    

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大数据松松

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

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

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

打赏作者

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

抵扣说明:

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

余额充值