跳至主要内容

dbt 快照:一份全面教程

在本教程中,了解什么是 dbt 快照、如何创建它,以及如何配置它来跟踪您的数据变更。
· 9分钟 阅读

使用 AI 探索

dbt(data build tool)是一款功能强大的工具,供数据分析师和数据工程师在现代数据栈中进行数据转换、测试与文档编制。 

尽管它以建模能力广为人知,另一个关键特性是 dbt 快照(snapshot),它使您能够在数据仓库中捕获缓慢变化维度(SCD)。

本教程将深入解释 dbt 快照、其使用场景及其实现方法。我将:

  • 定义什么是 dbt 快照。
  • 解释何时以及为何使用快照。
  • 逐步实现一个快照。
  • 可视化快照数据。
  • 分享一些最佳实践与挑战。

如需进一步了解 dbt,请务必查阅我们的指南 什么是 dbt?面向数据工程师的上手介绍,以及我们的 dbt 入门课程。  

什么是 dbt 快照?

dbt 快照是一种随时间跟踪记录变化的机制,尤其适用于那些默认不保留历史变更的表。快照主要用于实现缓慢变化维度(SCD)类型 2。当被跟踪的列发生变化时,它们会存储行的不同版本。 

使用场景包括:

  • 随时间跟踪客户画像的变化。
  • 监控运营数据中的状态变更。
  • 记录维度属性的演变,以支持准确的历史报表。

为何使用 dbt 快照?优势与用例

dbt 快照的价值在于,它们充当数据随时间变化的记录,使历史分析与合规报告成为可能。

优势

使用快照可带来以下一些好处:

  • 趋势分析:了解客户行为、属性或状态如何演变。
  • 审计与合规:向审计人员或监管机构展示历史数据状态。
  • 调试数据管道:识别记录何时发生意外变更,从而定位问题。

常见用例

在一些特定情境中,快照尤为有用,例如:

  • 跟踪忠诚度计划中的会员等级升级。
  • 记录零售产品的价格历史。
  • 捕获人力资源系统中员工部门变更。

架构基础

快照可与 dbt 生态体系无缝集成,借助其配置与运行管理,高效构建历史表。

以下以数据中使用的模型类型为例进行说明。

数据中主要有两类模型:

  • 标准模型:转换数据以生成当前状态表。
  • 快照模型:创建版本化表以跟踪历史变更。

这两类模型在用途、存储方式和应用上各有不同。

以下是它们差异的概要:

功能

标准模型

快照

目的

当前状态转换

历史跟踪

存储

当前数据的表或视图

仅追加的历史表

最佳适用

事实/维度模型

缓慢变化维度

dbt 快照的工作原理

快照通常遵循一套结构化流程,确保系统性地捕获所有变更,涉及以下步骤:

  1. 配置:定义快照策略、唯一键与变更检测列。
  2. 执行:运行 dbt snapshot 将当前数据与已有快照进行比较。
  3. 存储:dbt 为检测到的变更插入新记录,并更新有效期时间戳。

但 dbt 是如何跟踪变更的呢?

所有变更都会记录在输出的快照表中。

如果检测到变更,dbt 会将上一版本的 dbt_valid_to 字段更新为当前时间戳,并插入一行新记录,将 dbt_valid_from 字段设为当前时间戳。

dbt 中的快照策略

快照使用两种主要策略来检测变更。

1. 时间戳策略

  • 定义:使用时间戳字段检测变更。
  • 适用时机:源表具有可靠的 updated_at 时间戳。

2. 校验策略

  • 定义:直接比较指定的列。
  • 适用时机:源表缺乏可靠时间戳,但需要跟踪属性变更。

策略

检测方法

最佳适用

Timestamp

更新的时间戳列

具有可靠“最后修改”时间戳的表

Check

直接比较列值

没有时间戳列的表

硬删除

诸如删除在内的数据变化也可以被跟踪,但默认不启用。不过,可以通过一种方式来监控这类硬删除变更。

  • 问题:快照默认不跟踪被删除的行。
  • 解决方案:将 invalidate_hard_deletes=True 设置为真,以通过 dbt_valid_to 值标记被删除的行,从而保留其最后状态。

下面是一个实现校验策略并考虑硬删除的示例:

{%
  snapshot customers_snapshot %}

{%
  set config(
    target_schema='snapshots',
    unique_key='customer_id',
    strategy='check',
    check_cols=['tier'],
    invalidate_hard_deletes=True
  )
%}

SELECT * FROM {{ source('customers') }}

{% endsnapshot %}

dbt 快照实施指南

既然我们已经了解 dbt 快照的工作方式,下面来看如何实现它们 

1. 快照教程的示例数据集

为练习快照,我们将创建一个示例 customers 表,用以模拟现实世界的会员系统。这为我们提供了一些具体数据,以测试常见的快照策略。

import csv
from datetime import datetime

# Define the data
customers_data = [
    ["customer_id", "name", "tier", "updated_at"],  # header row
    [1, "Alice Tan", "Silver", "2025-07-01 10:00:00"],
    [2, "Bob Lee", "Gold", "2025-07-01 10:00:00"],
    [3, "Cheryl Lim", "Silver", "2025-07-01 10:00:00"]
]

# Write data to customers.csv
with open("customers.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    writer.writerows(customers_data)

2. 设置您的 dbt 项目

在实现快照之前,正确设置 dbt 项目至关重要。这可确保您的环境按结构化、可扩展的方式管理模型、快照、测试与文档。

先决条件

  • 确保已安装 Python 3.7+。
  • 使用 pip 安装 dbt:
pip install dbt

根据您的环境,将 dbt-postgres 替换为您的仓库适配器,例如 dbt-snowflake 或 dbt-bigquery。

分步设置说明

1. 导航到目标项目目录。

这里将存放您的分析或 ELT 项目。在本例中,我们会将所有相关文件存放在此 dbt 项目文件夹中。

cd ~/dbt-snapshot-project

2. 初始化一个新的 dbt 项目。

创建一个名为 dbt_project.yml 的配置文件:

将以下内容粘贴到项目文件中:

name: dbt_snapshots_project
version: '1.0'
profile: austinchia

seeds:
  dbt_snapshots_project:
    +quote_columns: false

3. 测试连接。

dbt debug

该命令应输出如下结果:

dbt debug output

4. 将种子数据加载到本地数据库。

运行以下命令:

dbt seed

该命令应输出如下结果:

dbt seed output

5. 配置数据库连接设置

在 profiles.yml 文件中,通常位于 ~/.dbt/profiles.yml。

以下是我的 profiles 文件内容:

austinchia:
  target: dev
  outputs:
    dev:
      type: sqlite
      threads: 1
      database: ./dbt_snapshots_project.db
      schema: main
      schema_directory: ./schemas
      schemas_and_paths:
        main: ./dbt_snapshots_project.db
      extensions:
        - "C:/sqlean/crypto.dll"

如果您与我一样使用SQLite 数据库,还需要安装SQLean 扩展。

6. 将数据导入 dbt 分层(staging)模型:

创建一个名为 models 的新文件夹。在该文件夹中创建一个名为 stg_customers.sql 的文件。

将以下代码粘贴到文件中:

SELECT
  *
FROM {{ ref('customers') }}

7. 在 dbt 中使用以下命令构建分层模型。

dbt run --select stg_customers

该命令应输出如下结果:

dbt staging output

3. 配置 dbt 快照

  1. 创建 snapshots/ 目录。
  2. 编写带有正确配置的快照 SQL 文件。

在本例中,您可以使用以下代码执行基于时间戳策略的快照。

{% snapshot customers_snapshot %}

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

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

{% endsnapshot %}

4. 运行 dbt 快照

执行:

dbt snapshot

该命令中,若快照表不存在,dbt 会创建之;同时会为检测到的变更插入新行。

该命令应输出如下结果:

dbt snapshot output

5. 检查快照表

现在您已执行了快照,项目文件目录中应生成一个新的 snapshot.db 文件。

我在 VSCode 中打开它,以查看该文件的样子:

snapshot table

如您所见,它包含了 dbt_updated_at 等附加列。这些列对于判定数据是否有变更以及变更发生的时间至关重要。

如需更多 dbt 的实操指南,请查看我们的dbt 语义层指南。

示例:使用 dbt 快照跟踪数据变更

快照允许团队随时间捕获任意表中任何属性变更的完整审计轨迹,因此在合规、调试与分析方面非常强大。

  1. 创建一个示例 customers 表及相关字段。包括客户 ID、姓名、邮箱与 updated_at 时间戳,用于跟踪变更发生的时间。
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100),
    updated_at TIMESTAMP
);
  1. 插入初始数据以模拟基线状态。

使用以下代码向表中插入数据。这些数据将作为变更前的原始数据。

INSERT INTO customers VALUES 
(1, 'Alice Tan', '[email protected]', '2025-07-01 10:00:00'),
(2, 'Bob Lee', '[email protected]', '2025-07-01 10:00:00');
  1. 编写一个 dbt 快照配置来跟踪邮箱变更:
{% snapshot customers_email_snapshot %}

{%
  set config(
    target_schema='snapshots',
    unique_key='customer_id',
    strategy='check',
    check_cols=['email'],
    invalidate_hard_deletes=True
  )
%}

SELECT * FROM {{ source('customers') }}

{% endsnapshot %}
  1. 运行 dbt snapshot 以记录初始状态。
dbt snapshot
  1. 在源表中模拟一次邮箱更新。

接下来,我们将通过修改一位客户的信息来更新原始表,以模拟数据变更。

UPDATE customers
SET email = '[email protected]', updated_at = '2025-07-02 09:00:00'
WHERE customer_id = 1;
  1. 再次运行 dbt snapshot 以捕获变更。
dbt snapshot
  1. 查询快照表。

要查看数据的差异与变更,您需要查询快照表。该表应显示邮箱地址的两个版本及其各自的有效期窗口:

SELECT customer_id, email, dbt_valid_from, dbt_valid_to
FROM snapshots.customers_email_snapshot
ORDER BY customer_id, dbt_valid_from;

此过程可确保您保留邮箱更新的完整变更历史,这对于 GDPR 审计、联系历史跟踪或客户支持分析至关重要。

高级配置与管理

有效的配置管理可让团队在项目与环境之间无缝扩展快照的实施。

1. 项目级与资源级配置

  • 项目级配置: 使用 dbt_project.yml 为所有快照设置默认配置(如目标 schema 或物化方式)。
  • 资源级配置: 在单个快照 SQL 文件中覆盖特定需求的设置,确保灵活性。

2. 自定义元数据字段

默认情况下,dbt 会创建 dbt_valid_from 和 dbt_valid_to 字段。若团队标准需要不同的命名以便在 BI 或数据治理工具中更清晰,您可以通过 valid_from 与 valid_to 配置项重命名它们。

3. 迁移旧版快照配置

若处理不当,迁移可能导致一些不一致。迁移快照配置时,需注意以下方面:

  1. 识别使用已弃用配置块样式的旧版快照定义。
  2. 若使用现代 dbt 版本,请重构为基于 YAML 的配置,以保持一致性。
  3. 验证正确性:
    • 在测试环境中运行快照。
    • 在退役旧快照前比较输出结果。
  4. 退役: 删除旧快照文件并相应更新文档。

4. 与其他工具集成

可将数据变更的跟踪与 dbt 快照协同进行。 

例如:

  • BI 工具(如 Power BI、Tableau): 构建显示属性随时间变化的时序可视化。
  • 可观测性平台(如 Monte Carlo、Datafold): 监控数据新鲜度与历史完整性。
  • 数据目录(如 Alation、Collibra): 将快照历史表作为合规所需的已记录血缘工件纳入目录。

生产环境实施的最佳实践

在生产环境中实施 dbt 快照需要经过深思熟虑的设计,以确保性能、可维护性与业务价值。

最好遵循以下做法:

1. 最佳用例与表选择

  • 优先选择具有缓慢变化属性的维度表,例如客户画像、产品主数据与供应商合同。
  • 避免针对事务表(如订单行项目)进行快照,除非必要,因为其高频插入与更新会导致存储快速增长。

2. 性能优化技巧

在生产中,您会希望优化数据,使变更跟踪高效且不影响产品。

以下是一些提示:

  • 索引: 在 unique_key 与有效期列上添加索引,以加快查询。
  • 分区: 按日期对快照表进行分区,以优化扫描性能。
  • 聚簇(特定仓库): 在高基数字段上使用聚簇键(例如 BigQuery 中),以提升过滤查询速度。

3. 维护与监控实践

  • 调度: 在非高峰时段运行快照,尽量降低计算成本影响。
  • 监控: 在 dbt Cloud 或您的编排工具中为快照失败设置告警。
  • 数据新鲜度检查: 使用 dbt 测试或可观测性工具验证快照数据的新鲜度。
  • 存储分析: 定期审查快照表大小,若业务允许则清理历史数据。

4. 常见陷阱

使用非唯一的 unique_key 会导致行版本控制不正确。请在部署前始终测试唯一性逻辑。 

当 存储不必要的历史变更(例如频繁更新时间戳的列)时,也可能发生快照膨胀,导致存储成本快速增长。

常见挑战与解决方案

在 dbt 中使用快照可能会遇到一些特定的技术挑战:

1. 唯一键冲突

问题: 由于唯一键定义不当,导致快照中出现重复行。

解决方案:

  • 在快照部署前使用 dbt 测试验证唯一性。
  • 必要时连接多个字段以确保复合唯一性。

2. 时态查询性能

问题: 在分析历史数据时查询缓慢。

解决方案:

  • 有效利用仓库分区与聚簇。
  • 始终按 dbt_valid_from 或日期范围过滤查询,以减少扫描数据量。

3. 排查快照执行错误

常见问题包括:

  • schema 或数据库权限不正确。
  • 源表结构变化(例如删除列)导致快照 SELECT 语句报错。
  • 快照配置块中的语法错误。

解决方案:

  • 使用 dbt debug 验证连接与配置。
  • 在 dbt 中实施 CI 测试,以在部署前捕获错误。
  • 查看 dbt 运行日志以获取详细错误信息。

结论

dbt 快照使分析团队能够以最小的工程投入构建稳健的历史数据集,支持合规分析。若您有兴趣进一步了解 dbt,欢迎查看我们的dbt 入门课程。若需阅读材料与更多指南,请查看我们的dbt-utils 包入门与dbt 教程。

dbt 快照常见问题

如何为不同的数据策略配置 dbt 快照?

要为不同的数据策略配置 dbt 快照,您可以在快照配置中选择时间戳(timestamp)或校验(check)策略。对于时间戳策略,指定 strategy='timestamp' 并提供能在任一变更时可靠更新的 updated_at 列。对于校验策略,使用 strategy='check' 并在 check_cols 中定义需要监控变更的列清单。

调度 dbt 快照的最佳实践是什么?

在非高峰时段进行调度,使频率与数据变更速率保持一致,使用 Airflow 或 dbt Cloud 等编排器,将其与繁重的转换作业分开运行,并定期监控错误。

如何在 dbt 快照中自定义元数据字段?

您可以在快照配置中重命名 dbt_valid_from 和 dbt_valid_to 等默认元数据字段,并通过 YAML 中的 meta 配置添加自定义元数据,以符合您的数据标准。

dbt 快照中的时间戳与校验策略有何区别?

时间戳策略使用 updated_at 列检测任意变更;而校验策略对特定列进行差异比较;前者更简单,后者在没有可靠时间戳时提供更强的控制力。

如何在 dbt 快照中处理被删除的记录?

使用 invalidate_hard_deletes=True,这样 dbt 会用有效期结束时间标记缺失记录,从而在快照历史中有效捕获删除。

Austin Chia

作者

Austin Chia

我是 Austin,一名博主和科技作者,曾在医疗领域担任数据科学家与数据分析师,拥有多年的从业经验。我以生物学背景开启技术之路,如今通过我的科技博客帮助他人完成同样的转型。我对技术的热情促使我为数十家 SaaS 公司撰写内容,分享经验、启发他人。

主题