双录入过渡表—我们如何跟踪Shopify的状态变化

162 阅读9分钟

双录入过渡表:我们如何跟踪Shopify的状态变化

最近,我们推出了Shopify Balance,这是一个资金管理账户和卡,让Shopify商户可以快速获得他们的资金,而且没有任何费用。在Shopify Balance的测试版推出后,Shopify数据团队被邀请来回答这个问题:我们如何可靠地计算使用Balance的商家的数量?特别是,我们如何在历史上计算这个数字?

虽然这听起来是一个简单的问题,但它对于了解我们的产品是否成功以及商家是否真正在使用它是至关重要的基础。这也比它看起来的答案要复杂得多。

要被认为是使用Shopify Balance,商家必须同时拥有一个活跃的Shopify Balance账户和一个活跃的Shopify账户。这意味着我们需要建立一些东西来同时跟踪两个账户的状态变化,并使这种跟踪随着时间的推移变得强大和可靠。输入双录入过渡表。虽然这是一个非常 "前期投资和长期节省大量时间 "的策略,但双录入过渡表让我们可以灵活地看到导致特定变化的个别输入。它做到了这一切,同时简化了我们的查询,减少了对我们报告的长期维护。

在这篇文章中,我们将探讨我们如何使用双录入过渡表建立一个数据管道来回答我们的问题:有多少Shopify商户在使用Shopify余额?我们将讨论我们如何设计一个可以随着我们产品的复杂性增长而扩展的东西,使用双录入过渡表的好处--从易用性到未来证明我们的报告--以及一些使用我们新表的样本查询。

什么是双录入过渡表?

双录入过渡表本质上是一种数据展示格式,它可以跟踪实体的属性随时间的变化。在Shopify,我们最早使用双录入过渡表的案例之一是用来跟踪商家使用平台的状态,使我们能够报告有多少商家拥有活跃的账户。与有从和到列的标准过渡表相比,双录入过渡表为每个状态变化输出两行,同时还有一个新的net_change 列。他们还可以将许多单独的跟踪属性合并到一个单一的输出中。

我花了很长时间来理解这个net_change 列,但它的工作原理是这样的:如果你想跟踪某个东西在一段时间内的状态,每当状态从一个状态变为另一个状态,或者反过来,都会有两个条目:

  1. net_change= -1: 这一行是以前的状态
  2. net_change= +1: 这一行是新的状态

双录入过渡表有很多优点,包括:

  • net_change 列是加法的:这是使用这种类型表格的真正好处。这允许你在过滤你关心的状态的同时,通过加总net_change ,快速得到处于某种状态的实体数量。
  • 识别变化的原因:对于你关心的整体状态(一个取决于几个基本状态的状态)的情况,你可以进入表格,看看哪个单独的属性引起了变化。
  • 保留所有的时间信息:输出保留了所有的时间信息,甚至可以正确地为具有相同时间戳的transitions 。这对于你需要知道某个特定状态的持续时间等情况很有帮助。
  • 容易用额外的属性进行扩展:如果下游的依赖关系写得正确,你可以在你追踪的产品的复杂性增长时,向你的表添加额外的属性。额外的好处是,你不必重写任何现有的SQL或PySpark,这都要归功于net_change 列的添加性质。

对于我们确定有多少商家在使用Shopify Balance的目的来说,双录入过渡表允许我们在一个表中跟踪Shopify Balance账户和Shopify账户的状态变化。它还为我们提供了一种简洁的方式来查询每个实体在一段时间内的状态。但我们如何做到这一点呢?

构建我们的双录入过渡管道

首先,我们需要准备个别属性表,作为我们的双录入过渡数据基础设施的输入。我们至少需要一个属性,但随着我们追踪的产品的增长,它可以扩展到任何数量的属性。

在我们的案例中,我们为Shopify余额账户状态和Shopify账户状态都创建了单独的属性表。一个属性输入表必须有一组特定的列:

  • 一个跨属性的分区键,在我们的例子中,它是一个account_id
  • 一个排序键,通常是一个transition_at 时间戳和一个index
  • 一个你想追踪的属性。

使用一个标准的过渡表,我们可以用一个简单的PySpark作业将其转换为一个属性。

注意index 列。我们使用一个行号窗口函数创建了这个index ,在我们的原始数据中有重复的account_idtransition_at 集的时候,都是由transition_id 来排序。虽然很简单,但如果有两个时间戳相同的过渡事件,它就起到了一个分界线的作用。这确保了我们的属性中始终有一个唯一的account_id,transition_at,index 集,以便对事件进行正确排序。index 在以后我们创建双录入过渡表时发挥了关键作用,确保我们能够捕捉到我们两个状态的顺序。

我们的Shopify Balance状态属性表显示一个加入和离开Shopify Balance的商家。

现在我们有了我们的两个属性表,是时候把这些输入我们的双录入过渡管道了。这个系统(称为构建合并状态transitions )采用我们的单个属性表,并首先使用partition_key (在我们的例子中,account_id 列)和sort_key (在我们的例子中,transition_atindex 列)生成一组组合的唯一行。然后,它为每个属性创建一个列,并按照partition_keysort_key 所定义的顺序,用各自表中的值来填充属性列。如果数值缺失,它将使用该属性的前一个已知值来填充表格。下面你可以看到两个属性的例子被合并到一起并填入。

两个例子的属性被合并到一个单一的输出表中。

然后这个表被运行到另一个过程中,这个过程创建了我们的net_change 列,并给所有当前的行分配了一个+1的值。它还为每个状态变化插入第二行,其值为net_change ,即-1。这个net_change 列现在表示每个状态变化的方向,如前面所述。

多亏了我们的管道,设置一个双录入过渡表是一个非常简单的PySpark工作。

注意在上面的代码中,我们已经指定了默认值。这些是用来填补属性的初始空值的。现在,下面是我们最终的双录入过渡表的输出,我们把它称为我们的 `accounts_transition_facts`表。该表记录了一个商家的Shopify和Shopify余额账户在一段时间内的状态。看一下shopify_status列,我们可以看到他们在2018年从activeactive ,而balance_status 列显示我们他们在2021年3月14日从not_on_balanceactive ,随后在2021年4月23日从activeinactive

一个加入和离开Shopify的商户在我们的accounts_transition_facts 双录入过渡表中的余额。

使用双录入过渡表

还记得我如何提到net_change 列是加法的吗?这使得使用双录入过渡表的工作变得非常容易。对net_change 列进行加法的能力大大减少了获得状态计数所需的SQL或PySpark。例如,使用我们新的account_transition_facts 表,我们可以通过Shopify Balance状态和Shopify状态,确定Shopify Balance上的active 账户总数。我们所要做的就是对我们的net_change 列进行求和,同时对我们所关心的属性状态进行过滤。

在日期栏上添加一个分组,我们就可以看到账户在一段时间内的净变化。

我们甚至可以在其他PySpark工作中使用这些输出。下面是一个PySpark工作消耗我们的account_transition_facts 表的输出的例子。在这种情况下,我们正在将账户号码的每日净变化添加到Shopify余额的每日快照汇总表中。

你有很多方法可以用SQL或PySpark实现同样的输出,但有一个双录入的过渡表在查询时大大简化了代码。而且如前所述,如果你使用加法net_change 列来编写代码,当你向双录入过渡表添加更多属性时,你将不需要重写任何SQL或PySpark。

我们不会撒谎,我们花了很多时间和精力来建立第一个版本的account_transition_facts 表。但由于我们的投资,我们现在有一个可靠的方法来回答我们最初的问题:我们如何计算使用余额宝的商家数量?用我们的双录入过渡表就很容易了!按我们关心的状态分组,简单地将net_change ,然后,我们就得到了答案。

我们的双录入过渡表不仅简单而优雅地回答了我们的问题,而且它也很容易与我们的产品一起扩展。由于net_change 列的加法性质,我们可以增加额外的属性而不影响任何现有的报告。这意味着这只是我们account_transition_facts 表的开始。在未来几个月,我们将评估其他随时间变化的状态,并将那些对Shopify Balance有意义的状态添加到我们的表中。下次你需要可靠地计算多种状态时,可以尝试探索双录入过渡表。