长期以来,Kimball方法一直是维度数据建模技术的标准。根据Kimball的说法,“时间概念渗透到数据仓库的每个角落”。这在数据分析的背景下意味着什么?在较高的层面上,现代分析可以被视为随着时间的推移不断变化的数据的聚合。问题在于,不断变化的数据不仅包括新的添加,还包括对先前数据集的更改。
整体维度数据建模将数据分为两大类:
事实——这些数据代表存储实体测量值的无限数据集。它包含定量分析和决策所必需的数据。事实表经常具有连接到其他表(维度)以供参考的列。
维度-该数据代表相对有限的数据集,提供有关事实表中执行的测量的描述性信息。与事实表相比,维度的发展速度要慢得多。这就是它们通常被称为“缓慢变化的维度”的原因。
Kimball的方法涉及根据事实和维度创建星型模式。由于非规范化结构,星型模式非常适合分析用例,不需要复杂的连接条件。因此,多年来,星型模式一直是传统数据仓库建模的事实上的标准。
多年来,数据处理程序一直面临着处理缓慢变化的维度而不丢失其以前的历史记录以及保留对事实表的关系引用的挑战。Kimball方法提出了几种有效处理缓慢变化维度(简称SCD)的方法。现实情况是,一旦选择了特定的SCD方法,在数据仓库中实施它就相对容易。对SQL和ACID事务的支持使其易于处理。
不幸的是,在数据湖中实现相同的情况却是另一回事。造成这种情况的原因有几个:
DeltaLake框架解决了上述问题。对ACID(原子性、一致性、隔离性和持久性)事务的支持现在可以像数据仓库一样轻松地实现SCD。在本文中,我们将了解如何使用DeltaLake框架实现解决缓慢变化的维度的最常见方法。
考虑下面的示例场景:
“一家公司希望跟踪客户维度随时间发生的变化。他们已要求数据工程团队提出一些替代方案。经过仔细考虑,数据工程提出了三个选项来管理缓慢变化的维度:SCDType1、SCDType2和SCDType3。”
在我们进入每个选项之前,让我们了解客户维度的数据结构。在本文中,我们将使用下面的示例数据集。下面的数据集显示了一些示例客户记录。为了解释处理缓慢变化的维度的不同选项,重点仍然是使用红色框突出显示的客户记录(名称为MageeCash的客户)。
MageeCash最近更改了她的地址。变更记录作为CDC记录传送到OLAP系统。在数据工程的背景下,CDC流程旨在从源捕获增量数据集并将它们合并到企业数据湖中。以下是MageeCash的变更记录,注意地址与上面的原始记录不同。
DeltaLake的核心能力使其成为构建现代数据湖屋架构的极其合适的平台。在Lakehouse架构中,DeltaLake可用于将变更记录合并到公共数据层中。创建后,公共层将充当分析工作负载(包括BI、数据科学、机器学习和人工智能)的基础数据层。因此,公共层通常被称为“单一事实来源”。
让我们回到本文的核心目标。现在我们对数据集有了清晰的了解,我们准备探索第一个SCD方法。
SCD1型
这种类型通常称为“覆盖”方法。在此方法中,对维度数据的任何更改都会简单地覆盖具有相同键的数据的先前状态。尽管实现起来非常简单,但该方法有一个主要缺点。由于覆盖机制,您不仅会丢失维度的先前历史记录,还会丢失它所附加到的事实表的状态。使用SCD类型1方法的客户维度的前后图像如下所示。
请注意,新的家庭地址是如何简单地覆盖以前的地址的,以前的地址的历史记录会丢失。在事实表聚合受到维度变化影响的情况下,丢失历史记录的影响可能会很严重。在这种情况下,如果没有历史记录,就很难追溯聚合值受到影响的原因。
现在我们将了解如何使用Delta框架实现SCDType1。首先使用Lakehouse贴源层中的原始客户数据集创建silver层客户维度表(customer_silver_scd1)。
使用MageeCash的更改记录创建一个新的数据框。
最后将地址变更记录合并到customer_silver_scd1 silver层维度表中。
对silver层维度表执行查询后,会发现地址的更改已覆盖其之前的状态。问题是这条记录之前的状态已经无处可见。
考虑这样一个场景:MageeCash可能使用以前版本的地址下了电子商务订单。产品尚未发货,但地址已更改。产品应该运到哪里?旧地址或新地址。
DeltaLake维护按时间顺序排列的更改历史记录,包括插入、更新和删除。在上面的示例中,表的版本0是在创建customer_silver_scd1silver层表时生成的。同样,当我们对地址记录变更进行数据合并时,创建了表的版本1。此外,DeltaLake表可以根据需要轻松恢复到任何以前的版本。
由于上述缺陷,SCDType1很少在现代数据平台中使用。因此,我们需要一种更好的方法,使我们能够对维度进行更改,同时保留以前的引用以供主动使用。总的来说,如果计算不关心数据的先前状态或其导致的影响,则只需使用SCD类型1。
SCD2型
也称为“添加新记录”方法。在此方法中,更改记录将作为新记录添加到维度表中,并标记为“当前”或“活动”。此外,先前版本的记录被标记为“已过期”或“无效”。记录的各个版本(当前版本和历史版本)使用代理键绑定在一起。在表级别,SCD类型2是通过为维度表中的每一行添加StartDate和EndDate时间戳列来实现的。此外,还添加了“状态”列来标记记录是最新的还是已过期地位。使用SCD类型2方法的客户维度的前后图像如下所示。
我们现在将了解如何使用delta框架来实现SCD类型2。首先使用Lakehouse贴源层中的原始客户数据集创建silver层客户维度表(customer_silver_scd2)。
现在将地址变更记录合并到customer_silver_scd2 silver层维度表中。
请注意,之前的记录已标记为“已过期”,并且结束日期已更新。此外,还插入了具有最新地址的新记录,其开始日期与前一条记录的结束日期相同。使用这种方法,MageeCash肯定会将她的电子商务订单运送到正确的地址。
使用SCD类型2方法,可以按时间顺序跟踪随时间变化的历史记录,并按时间顺序维护对事实表的引用。必须承认,与SCDType1相比,其实现有点棘手。
需要注意的是,维护维度表的应用程序需要以这样的方式进行编码,即在一个事务中执行当前版本的新记录的添加和先前版本的到期。此外,每个针对维度表的查询都需要过滤status=Current。
还有一个更简单的替代方案,我们进一步探索另一种方法,它在某些方面只是SCD类型1方法的扩展。
SCD3型
也称为“添加新字段”方法。对于每次更改,先前版本和当前版本都存储为维度表同一行中的两个不同列。与SCDType2相比,SCDType3相对更容易实现,历史记录仅包括当前版本和以前的版本。
我们现在将了解如何使用delta框架来实现SCDType3。首先使用Lakehouse贴源层中的原始客户数据集创建silver层客户维度表(customer_silver_scd3)。
请注意,维度表中的每一列都维护当前和先前的状态。在创建维度表时,列的当前状态将填充最新数据,而列的先前状态将保留为空。
现在将地址变更记录合并到customer_silver_scd3 silver层维度表中。
继续检查合并后的记录状态。
请注意,地址字段现在已填充有更改的记录,并且地址的先前版本已移至previous_address字段。同样,modifieddate字段已更新以维护更改时间顺序。
事实上,只有有限数量的历史记录可用,这使得SCD类型3的用例有点有限。但实施的简便性使其有些令人向往。如果您讨厌SCDType1的局限性并且发现SCDType2难以实施和管理,那么这是一个很好的权衡。
在许多方面,SCD2型通常被认为是实现缓慢变化维度的主要技术。应该清楚地理解,SCD的主要目标不是存储记录的历史记录,而是保持与事实表的准确关联。此外,在许多方面,缓慢变化的维度要求更新记录,这通常违背了数据湖/仓库的不可变性质的原则。然而,DeltaLake等框架取得的新进展使得实现SCD场景成为可能。