Skip to content

快照

简介

什么是快照?

分析师通常需要“及时回顾”其可变表中以前的数据状态。虽然一些源数据系统的构建方式使访问历史数据成为可能,但情况并非总是如此。dbt提供了一种机制,快照,它记录随着时间的推移对可变表的更改。

快照执行type-2 缓慢变化的维度在可变表上。这些缓慢变化的维度标识了表中的记录如何随时间变化。假设您有一个orders(订单)表,在处理订单时可以覆盖status(状态)字段。

id status updated_at
1 pending 2019-01-01

现在,假设订单从“pending(待定)”变为“shipped(已发货)”。同样的记录现在看起来是:

id status updated_at
1 shipped 2019-01-02

该订单现在处于“已发货”状态,但我们已经丢失了该订单最后一次处于“待定”状态的信息。这使得很难(或不可能)分析订单发货所需的时间。dbt可以“快照”这些更改,以帮助您了解一行中的值是如何随时间变化的。以下是上一个示例的快照表示例:

id status updated_at dbt_valid_from dbt_valid_to
1 pending 2019-01-01 2019-01-01 2019-01-02
1 shipped 2019-01-02 2019-01-02 null

在dbt中,快照是select语句,在.sql文件中的快照块中定义(通常在“snapshots”目录中)。您还需要配置快照来告诉dbt如何检测记录更改。

{% snapshot orders_snapshot %}

{{
    config(
      target_database='analytics',
      target_schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at',
    )
}}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

在IDE中预览或编译快照

在dbt Cloud中无法为快照“预览数据”或“编译sql”。但可以通过完成以下步骤,在IDE中运行`dbt snapshot`命令。

当您运行dbt snapshot命令时:

  • 第一次运行: dbt将创建初始快照表-这将是select语句的结果集,其他列包括dbt_valid_fromdbt_valid_to。所有记录都将有一个dbt_valid_to=null
  • 在随后的运行中: dbt将检查哪些记录已更改或是否创建了任何新记录:
    • 对于任何已更改的现有记录,都将更新dbt_valid_to
    • 更新后的记录和任何新记录都将插入到快照表中。这些记录现在将具有dbt_valid_to=null

快照可以在下游模型中引用,方法与引用模型相同——使用ref函数。

示例

在你的项目中添加一个快照:

1.在snapshots目录中创建一个.sql文件,比如snapshots/orders.sql 2.使用snapshot块定义快照的开始和结束:

{% snapshot orders_snapshot %}

{% endsnapshot %}

3.在快照块中编写select语句(下面是编写好的快照查询的技巧)。此select语句定义了要随时间进行快照的结果。你可以在这里使用sources来源refs参考

{% snapshot orders_snapshot %}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

4.检查查询的结果集是否包含可靠的时间戳列,该列指示记录上次更新的时间。对于我们的示例,updated_at列可靠地指示记录更改,因此我们可以使用timestamp策略。如果您的查询结果集没有可靠的时间戳,则需要使用检查策略,详细信息如下。

5.使用配置块将配置添加到快照中(详细信息如下)。您也可以从dbt_project.yml文件配置快照。

{% snapshot orders_snapshot %}

{{
    config(
      target_database='analytics',
      target_schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at',
    )
}}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

6.运行dbt snapshot命令,正如我们举例的那样,这将在analycy.snapshot.orders_snapshot中创建一个新表。您可以更改target_database配置、target_schema配置和快照名称(如{% snapshot .. %}中所定义),以更改dbt命名此表的方式。

$ dbt snapshot
Running with dbt=0.16.0

15:07:36 | Concurrency: 8 threads (target='dev')
15:07:36 |
15:07:36 | 1 of 1 START snapshot snapshots.orders_snapshot...... [RUN]
15:07:36 | 1 of 1 OK snapshot snapshots.orders_snapshot..........[SELECT 3 in 1.82s]
15:07:36 |
15:07:36 | Finished running 1 snapshots in 0.68s.

Completed successfully

Done. PASS=2 ERROR=0 SKIP=0 TOTAL=1

7.通过从创建的dbt表中进行选择来检查结果。第一次运行后,您应该会看到查询的结果,以及如下所述的快照元字段,如下所述。

8.再次运行snapshot命令,并检查结果。如果有任何记录发生更新,则快照会反映出来。

9.使用ref函数从下游模型中的snapshot中进行选择。

select * from {{ ref('orders_snapshot') }}

10.安排snapshot命令定期运行, 只有频繁运行快照,快照才有用。

检测记录变化

快照“策略”定义了dbt如何知道一行记录是否发生了更改。dbt内置了两种策略——timestampcheck

Timestamp策略 (推荐)

timestamp策略使用updated_at字段来确定一行是否已更改。如果为一行配置的updated_at时间戳列比上次运行快照时有更新,那么dbt将使旧记录无效并记录新记录。如果时间戳没有改变,那么dbt将不会采取任何操作。

timestamp策略需包含以下配置:

Config Description Example
updated_at A column which represents when the source row was last updated updated_at

示例使用方法:

{% snapshot orders_snapshot_timestamp %}

    {{
        config(
          target_schema='snapshots',
          strategy='timestamp',
          unique_key='id',
          updated_at='updated_at',
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

Check策略

check策略对于没有真实的updated_at列的表很有用。此策略通过比较列的当前值和历史值来工作。如果这些列中的任何一列发生了更改,那么dbt将使旧记录无效并记录新记录。如果列值相同,那么dbt将不会采取任何操作。

check策略需包含以下配置:

Config Description Example
check_cols A list of columns to check for changes, or all to check all columns ["name", "email"]

check_cols = 'all'

check快照策略可以配置为通过提供check_cols='all'来跟踪 所有 列的更改。最好明确列出要检查的列。考虑使用"surrogate-key"将多个列压缩为一列。

示例

{% snapshot orders_snapshot_check %}

    {{
        config(
          target_schema='snapshots',
          strategy='check',
          unique_key='id',
          check_cols=['status', 'is_cancelled'],
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

硬删除(附加选择)

默认情况下,从SQL执行中删除的记录是有效的。使用配置选项invalidate_hard_deletes,dbt可以跟踪不再存在的记录。这是通过将快照表与源表里德left join,并筛选在该点仍然有效但在源表中找不到的行来完成的。dbt_valid_to将被设置为当前快照时间。

此配置与上述策略不同,而是一个附加的选择加入功能。默认情况下不会启用它,因为它会更改以前的行为。

要使此配置与timestamp策略配合使用,配置的updated_at列必须是时间戳类型。否则,查询将因数据类型混合而失败。

示例

{% snapshot orders_snapshot_hard_delete %}

    {{
        config(
          target_schema='snapshots',
          strategy='timestamp',
          unique_key='id',
          updated_at='updated_at',
          invalidate_hard_deletes=True,
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

配置快照

快照配置

有许多特定于快照的配置:

Config Description Required? Example
target_database dbt会将快照表输出的数据库 No analytics
target_schema dbt会将快照表输出的数据库schema Yes snapshots
strategy 快照策略,timestampcheck Yes timestamp
unique_key "primary-key"或表达式 Yes id
check_cols 如果使用check策略,就检查相应的字段 仅使用于check策略 ["status"]
updated_at 如果使用timestamp策略, 对比timestamp字段 仅使用于timestamp策略 updated_at
invalidate_hard_deletes 查找源数据中硬删除的记录, 如果不存在则将dbt_valid_to 更新为当前时间 No True

还支持许多其他配置(例如tagspost-hook),请查看完整列表

快照可以从dbt_project.yml文件和config块中进行配置,有关详细信息,请查看配置文档

注意:BigQuery用户可以使用target_projecttarget_dataset分别作为target_dabasetarget _schema的别名。

配置最佳实践

尽可能使用timestamp策略

此策略比check_cols策略能更好地处理列的添加和删除。

确保你的unique key是真的unique

dbt使用unique key来匹配行,因此确保该键实际上是唯一的非常重要!如果您正在对源数据进行快照,我建议您在源数据中添加一个唯一性测试(示例).

使用一个不同于你分析schema的target_schema

快照不能重新创建。因此,最好将快照放在一个单独的schema中,这样最终用户就知道它们是有特别用处的。您可能希望在快照上设置与模型不同的权限,甚至以不同的用户(或角色,取决于您的仓库)运行快照,以使删除快照变得非常困难。

快照查询最佳实践

快照源数据.

您的模型应该从这些快照中进行查询,将它们视为常规数据源。尽可能以原始形式快照源数据,并使用下游模型清理数据

在查询里使用source函数.

这有助于理解项目中的数据血缘。

包括尽可能多的字段.

事实上,如果性能允许,请使用select *,即使一个字段目前感觉不太有用,最好对它进行快照,以防它变得有用——毕竟,以后您将无法重新创建该字段。

避免在快照中使用join.

Join会使构建可靠的updated_at时间戳变得困难。相反,可以分别对这两个表进行快照,并将它们连接到下游模型中。

限制查询中的转换量.

如果您在快照查询中应用业务逻辑,并且该逻辑在未来发生变化,那么将逻辑中的变化应用于快照可能是不提倡的(或者至少非常困难)。

基本上,让您的查询尽可能简单!这些建议的一些合理例外情况包括:

  • 如果表很宽,则选择特定字段.
  • 进行轻度转换以使数据形成合理的形式,例如,将json包拆成字段.

快照元字段

快照表将作为源数据的克隆表创建,外加一些额外的元字段

Field Meaning Usage
dbt_valid_from 这一条记录被快照的创建时间 这个字段用来对某条记录的不同"版本"进行排序.
dbt_valid_to 这一条记录失效的时间. 最新的快照记录会设置成null.
dbt_scd_id 为每个快照记录生成的唯一ID 这是dbt内部使用的
dbt_updated_at 插入此条快照记录的时间戳. 这是dbt内部使用的
  • 根据您使用的策略,每列使用的时间戳略有不同: 对于timestamp策略,配置的updated_at列用于填充dbt_valid_fromdbt_valid_todbt_updated_at字段。
timestamp策略详情

2019-01-01 11:00查询结果

id status updated_at
1 pending 2019-01-01 10:47

快照结果 (注意11:00还没有地方看到):

id status updated_at dbt_valid_from dbt_valid_to dbt_updated_at
1 pending 2019-01-01 10:47 2019-01-01 10:47 2019-01-01 10:47

2019-01-01 11:30查询结果:

id status updated_at
1 shipped 2019-01-01 11:05

快照结果 (注意11:30 还没有地方看到):

id status updated_at dbt_valid_from dbt_valid_to dbt_updated_at
1 pending 2019-01-01 10:47 2019-01-01 10:47 2019-01-01 11:05 2019-01-01 11:05
1 shipped 2019-01-01 11:05 2019-01-01 11:05 2019-01-01 11:05


针对check策略,当前时间戳用于填充每一列。如果配置了,check策略则会像timestamp策略使用updated_at列。

check策略详情

2019-01-01 11:00快照查询结果

id status
1 pending

快照结果:

id status dbt_valid_from dbt_valid_to dbt_updated_at
1 pending 2019-01-01 11:00 2019-01-01 11:00

2019-01-01 11:30查询结果:

id status
1 shipped

快照结果:

id status dbt_valid_from dbt_valid_to dbt_updated_at
1 pending 2019-01-01 11:00 2019-01-01 11:30 2019-01-01 11:30
1 shipped 2019-01-01 11:30 2019-01-01 11:30