在本文中,我们将介绍缓慢变化的维度,也称为SCD。它是处理不断变化的数据的不同方法。但它有更多的细微差别,以及多种方法可供选择。如果您对此感兴趣,以及使用大量的可视化,请坚持下去!
我们将在本文中介绍以下内容:
关键定义
什么是SCD
为什么SCD很重要
SCD的类型
结论
关键定义
在开始之前,请务必了解某些关键概念:
数据仓库
针对分析优化的数据存储。其结构通常由事实表和维度表组成。
事实表
这是一个包含事件发生后的关键维度的表格。
例如,在社交媒体网站/应用程序中,一个名为“post”的事实表可用于捕获与任何帖子活动相关的数据及其所有关键维度。
这包括user_id、时间戳、device_id等:
维度表
我们在事实表“post”中设置了多个属性。如果我们想要有关此表的某个属性(例如user_id)的更多详细信息,该怎么办?
这就是维度表的用武之地。维度表是包含事实数据表中单个维度的更多信息的表,例如事实数据表中的user_id列。
此表将包含有关用户的辅助数据,例如他们的名字、姓氏、出生日期、他们创建帐户的时间戳、他们的电话号码等。
这种表结构的好处是,事实表永远不会因列过多而膨胀,但如果需要更精细的数据,则无需联接!
注意:上面的结构捕获了所谓的星型架构。对于雪花架构,我们还有子维度表。
OLAP
顾名思义,在线分析处理是一种用于大规模分析的数据方法。
如果您熟悉上述定义,您可能已经注意到它们都是特定于OLAP的概念。这是因为SCD仅适用于OLAP。不是OLTP。为什么?
在OLTP(联机事务处理)中,我们优先考虑1.无数据冗余和2.容纳最新版本的数据。没有灵活性的余地,所以我们只是覆盖数据。
因此,本文将以OLAP为中心。
使用OLAP,我们可以选择冗余和历史数据。当新数据传入时,这为我们提供了更多的策略选择,我们将在后面的示例中看到这一点。
什么是SCD
在这一点上,您现在希望了解什么是维度表。
请记住,在文章开头,我们所说的SCD在高层次上是:处理更改数据的不同方法。
现在我们有了上下文,更具体地说,缓慢变化维度(SCD)是专门处理维度表中不断变化的数据的不同方法。
假设您有以下user_id维度表:
注意:为简单起见,我们已从前面的user_id示例中删除了一些字段。
在此示例中,主键(行唯一的值)为user_id。然后,我们的数据管道会处理包含以下信息的新数据:
我们注意到,一些新数据与现有数据具有相同的主键(user_id)。尼古拉斯·凯奇(NicholasCage)和杰克·佩拉尔塔(JakePeralta)都有新的手机号码。
这是不断变化的数据。
您可能有一些初始问题:
为什么它“缓慢”发生?为什么不是事实表呢?为什么尼古拉斯凯奇在这里?
让我们一一解决这些问题。
为什么它“缓慢”发生?
形容词“缓慢”可能来自对这些维度更新不会过于频繁的期望。但这不是一个严格的规则。
为什么不是事实表呢?
如“键定义”部分所述,事实数据表是事件驱动的。一旦事件发生,除非时间过程是可能的,否则它是不可逆转的!因此,此处的更改不适用。
例如,当有人发布了微博故事时,现在已经发生了,并且无法更改。如果该用户删除了微博故事,则这是一个单独的事件,即事实表中的另一行。
为什么尼古拉斯凯奇在这里?
他是一个伟大的演员!我喜欢他在《无法承受的重量》中的表演。
为什么SCD很重要
现在你可能会想:为什么改变数据很重要?
让我们参考前面的例子。您有一个包含一些现有数据的数据库,并且有一组新数据进入。其中一些与您现有的数据相冲突,正如我们在NicholasCage的手机号码中看到的那样:
原始记录和新记录之间存在冲突。我们优先考虑哪一个?我们是只保留一个记录,还是同时保留两个记录?
这就是定义SCD至关重要的地方。根据团队中的业务需求和/或最佳数据工程实践,您最终会选择一种策略来处理此类情况。
注意:为简单起见,我们已从示例中删除了user_id列。但它是帮助数据管道确定哪些维度正在发生变化的关键列。
SCD的类型
有哪些不同的策略,我们如何决定哪种策略?
有许多策略-7种类型。然而,由于不切实际,有些策略从未使用过,因此我们将只关注本文中的几个关键策略。
本文将提供一些示例来帮助理解,并将提供一些关于何时使用哪种策略的指导。
SCD-0:无更新
说明:更新的数据行进来了,但我们忽略了它,并且不做任何更改。
推理:我们有一些我们预计永远不会改变的维度,所以这个新数据很可能是重复的,或者只是有缺陷的数据。
示例:如果我们假设用户第一次正确输入,我们绝不希望他们的出生日期发生变化。除非你伪造出生证明,否则这是不合理的。
因此,如果提出任何更改,例如:
具有新出生日期值的行
我们选择忽略这些更改并保持数据不变。
SCD-0如何处理更新
思考:
我发现这个SCD的适用性有限,因为很少有字段永远不会改变。
即使对于像date_of_birth这样的字段,用户也可能在第一次输入错误,因此我们需要我们的数据管道能够处理这些边缘情况。
SCD-1:覆盖
解释:进入更新的数据行,然后我们完全覆盖现有行。
推理:我们的维度只关心当前状态,因此我们删除了任何过时的行,转而使用更新的行。
例:尼古拉斯·凯奇(NicholasCage)和杰克·佩拉尔塔(JakePeralta)再次更新了他们的数据。这一次,他们换了手机号码。
具有新手机号码值的行
由于我们只关心他们的手机号码来联系他们,因此我们不再需要在user_id表中使用他们的旧电话号码。
SCD-1如何处理更新
思考:
这种SCD策略可以证明是有用的,尤其是当您试图避免表格膨胀时。
但是,丢弃数据总是有风险的,因为扭转此决定并恢复数据并不总是那么容易。需求将来总是会发生变化,所以我会谨慎使用它。
SCD-2:全部保留(基于行)
解释:将出现更新的数据行,因此我们将新行追加到表中。我们不会删除任何数据。为了帮助区分这两者,我们最多有3个新列:
开始日期列-数据管道处理此条目的时间
结束日期列-此条目何时结束(如果尚未结束,您可以选择31-12-9999,以表明它在我们的有生之年永远不会结束)
版本号列-1表示第一个版本,然后在每次更新时向上递增1
因此,维度表的结构会按照以下策略进行更改:
带有SCD-2的表结构
推理:我们有想要更新的维度,但我们不希望在此过程中丢失历史数据。
示例:NicholasCage和JakePeralta于2024年2月02日(01年2024月<>日)更新了他们的地址。
具有新邮政编码值的行
我们想要更新表格,但是我们希望保留旧数据,以防我们想要运行一些历史分析。
例如,我们可能想要观察用户多年来的位置分布。在2020年将尼古拉斯·凯奇放在他的新地址是没有意义的,因为他当时并不住在那里。因此,我们保留了新旧数据,并保留了版本、start_date和end_date列,以帮助区分两者。
SCD-2如何处理更新
思考:
SCD-2是我首选的维度数据更改方法之一。
您以易于阅读和解释的方式维护所有数据。
即使您最终只使用最新版本或第一个版本,也可以通过筛选版本或end_date轻松实现。
该策略也是可扩展的,因为您可以轻松地处理将来对相同字段的更改:为每个新版本添加新行。
SCD-3:全部保留(基于列)
解释:进入更新的数据行,然后我们使用新列进行跟踪。我们不是只为维度一列,而是将其拆分为2列-“当前”列和“上一个”列:
带有SCD-3的表结构
与SCD-2类似,我们不会删除任何数据。
推理:我们有想要更新的维度,但我们不希望在此过程中丢失历史数据。
例:尼古拉斯·凯奇(NicholasCage)和杰克·佩拉尔塔(JakePeralta)更新了他们的地址:
具有新邮政编码值的行
我们所做的是:
将“current_post_code”中的所有值复制到“previous_post_code”
然后替换“current_post_code”中具有新值的所有值:
SCD-3如何处理更新
思考:
如果我们期望在用户的一生中只发生一次变化,那么SCD-3是合适的。但是,这很难保证。
一些文章讨论了SCD-3的变体,这些变体包含更多列以说明更多版本。例如,使用“previous_3_post_code”列捕获第三个最新值。
在我看来,SCD-3不能很好地随着数据的变化而扩展,在这一点上,选择SCD-2策略更有意义。
SCD-4:全部保留(历史记录表)
解释:我们没有将所有更改的数据保存在一个维度表中,而是创建了一个额外的“迷你维度”历史记录表。
这样,当更新的数据行传入时,我们会做两件事:
覆盖当前表(类似于SCD-1)
更新历史表以跟踪以前的值(类似于SCD-2)
这个“历史表”包含什么?
主键列-允许您联接回当前表(为简单起见,下图中省略了)
维度表中的其他列-因为您正在捕获这些列的历史值(例如post_code)
开始日期列-数据管道处理此条目的时间
结束日期列(可选)-此条目何时结束(如果尚未结束,您可以选择31-12-9999,以指示它在我们的生命周期中永远不会结束)
版本号列(可选)-1表示第一个版本,然后在每次更新时向上递增1
历史表
推理:我们有想要更新的维度,但我们不希望在此过程中丢失历史数据。我们还希望维护所有当前数据的快照。
例:尼古拉斯·凯奇(NicholasCage)和杰克·佩拉尔塔(JakePeralta)再次更新了他们的地址:
具有新帖子代码值的行
如解释中所述,我们相应地更新了当前和历史表格:
SCD-4如何处理更新(当前表)
SCD-4如何处理更新(历史表)
思考:
SCD-4是我最喜欢的另一种方法,这个版本与SCD-2有很多相似之处。
现在你可能会问:那我该如何在两者之间做出选择呢?
在SCD-2和SCD-4之间进行选择时,需要考虑两个关键问题:
维度更新的频率如何?
在整篇文章中,我们的示例只介绍了1个用户更新。通过超过2次更新,我们已经确定SCD-2比SCD-3更合适。
但是,如果用户的邮政编码有多达100次更新怎么办?甚至更多?如果这种情况发生在数百万用户身上,这在微博这样的巨大平台上是可能的?
使用SCD-2,我们可以捕获更改,但user_id维度表将变得笨拙,尤其是在您一开始只需要当前数据的情况下。
这就是SCD-4的亮点所在:它为用户提供了灵活性。如果要执行历史分析,它确实需要对历史表进行额外的联接,因此请记住这一点。
下游分析是否始终依赖于历史数据或当前数据?
如果下游数据用户经常使用历史邮政编码数据,则SCD-4意味着他们每次都必须在当前表和历史表之间执行连接。
SCD-2将跳过该步骤并减少查询执行时间和群集资源。
但是,如果专门针对当前数据的查询频率相当高,则SCD-2将意味着用户每次都必须过滤他们的查询。
SCD-4意味着用户只需使用当前表,跳过该步骤,并减少查询执行时间和集群资源。
结论
总之,SCD是处理维度表中数据更改的不同方法。每种方法在不同的方案中都很有用,最终决定取决于你的要求,因此请花时间了解你的数据,然后相应地应用正确的SCD策略。