dbt(data build tool)是一款功能强大的工具,供数据分析师和数据工程师在现代数据栈中进行数据转换、测试与文档编制。
尽管它以建模能力广为人知,另一个关键特性是 dbt 快照(snapshot),它使您能够在数据仓库中捕获缓慢变化维度(SCD)。
本教程将深入解释 dbt 快照、其使用场景及其实现方法。我将:
- 定义什么是 dbt 快照。
- 解释何时以及为何使用快照。
- 逐步实现一个快照。
- 可视化快照数据。
- 分享一些最佳实践与挑战。
如需进一步了解 dbt,请务必查阅我们的指南 什么是 dbt?面向数据工程师的上手介绍,以及我们的 dbt 入门课程。
什么是 dbt 快照?
dbt 快照是一种随时间跟踪记录变化的机制,尤其适用于那些默认不保留历史变更的表。快照主要用于实现缓慢变化维度(SCD)类型 2。当被跟踪的列发生变化时,它们会存储行的不同版本。
使用场景包括:
- 随时间跟踪客户画像的变化。
- 监控运营数据中的状态变更。
- 记录维度属性的演变,以支持准确的历史报表。
为何使用 dbt 快照?优势与用例
dbt 快照的价值在于,它们充当数据随时间变化的记录,使历史分析与合规报告成为可能。
优势
使用快照可带来以下一些好处:
- 趋势分析:了解客户行为、属性或状态如何演变。
- 审计与合规:向审计人员或监管机构展示历史数据状态。
- 调试数据管道:识别记录何时发生意外变更,从而定位问题。
常见用例
在一些特定情境中,快照尤为有用,例如:
- 跟踪忠诚度计划中的会员等级升级。
- 记录零售产品的价格历史。
- 捕获人力资源系统中员工部门变更。
架构基础
快照可与 dbt 生态体系无缝集成,借助其配置与运行管理,高效构建历史表。
以下以数据中使用的模型类型为例进行说明。
数据中主要有两类模型:
- 标准模型:转换数据以生成当前状态表。
- 快照模型:创建版本化表以跟踪历史变更。
这两类模型在用途、存储方式和应用上各有不同。
以下是它们差异的概要:
|
功能
|
标准模型
|
快照
|
|
目的
|
当前状态转换
|
历史跟踪
|
|
存储
|
当前数据的表或视图
|
仅追加的历史表
|
|
最佳适用
|
事实/维度模型
|
缓慢变化维度
|
dbt 快照的工作原理
快照通常遵循一套结构化流程,确保系统性地捕获所有变更,涉及以下步骤:
- 配置:定义快照策略、唯一键与变更检测列。
- 执行:运行
dbt snapshot 将当前数据与已有快照进行比较。
- 存储: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
customers_data = [
["customer_id", "name", "tier", "updated_at"],
[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"]
]
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技术支持
该命令应输出如下结果:

4. 将种子数据加载到本地数据库。
运行以下命令:
dbt seed技术支持
该命令应输出如下结果:

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技术支持
该命令应输出如下结果:

3. 配置 dbt 快照
- 创建 snapshots/ 目录。
- 编写带有正确配置的快照 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 会创建之;同时会为检测到的变更插入新行。
该命令应输出如下结果:

5. 检查快照表
现在您已执行了快照,项目文件目录中应生成一个新的 snapshot.db 文件。
我在 VSCode 中打开它,以查看该文件的样子:

如您所见,它包含了 dbt_updated_at 等附加列。这些列对于判定数据是否有变更以及变更发生的时间至关重要。
如需更多 dbt 的实操指南,请查看我们的dbt 语义层指南。
示例:使用 dbt 快照跟踪数据变更
快照允许团队随时间捕获任意表中任何属性变更的完整审计轨迹,因此在合规、调试与分析方面非常强大。
- 创建一个示例
customers 表及相关字段。包括客户 ID、姓名、邮箱与 updated_at 时间戳,用于跟踪变更发生的时间。
CREATE TABLE customers (
customer_id INTEGER PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
updated_at TIMESTAMP
);技术支持
- 插入初始数据以模拟基线状态。
使用以下代码向表中插入数据。这些数据将作为变更前的原始数据。
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');技术支持
- 编写一个 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 %}技术支持
- 运行
dbt snapshot 以记录初始状态。
dbt snapshot技术支持
- 在源表中模拟一次邮箱更新。
接下来,我们将通过修改一位客户的信息来更新原始表,以模拟数据变更。
UPDATE customers
SET email = '[email protected]', updated_at = '2025-07-02 09:00:00'
WHERE customer_id = 1;技术支持
- 再次运行
dbt snapshot 以捕获变更。
dbt snapshot技术支持
- 查询快照表。
要查看数据的差异与变更,您需要查询快照表。该表应显示邮箱地址的两个版本及其各自的有效期窗口:
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. 迁移旧版快照配置
若处理不当,迁移可能导致一些不一致。迁移快照配置时,需注意以下方面:
- 识别使用已弃用配置块样式的旧版快照定义。
- 若使用现代 dbt 版本,请重构为基于 YAML 的配置,以保持一致性。
- 验证正确性:
- 在测试环境中运行快照。
- 在退役旧快照前比较输出结果。
- 退役: 删除旧快照文件并相应更新文档。
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(data build tool)是一款功能强大的工具,供数据分析师和数据工程师在现代数据栈中进行数据转换、测试与文档编制。
尽管它以建模能力广为人知,另一个关键特性是 dbt 快照(snapshot),它使您能够在数据仓库中捕获缓慢变化维度(SCD)。
本教程将深入解释 dbt 快照、其使用场景及其实现方法。我将:
如需进一步了解 dbt,请务必查阅我们的指南 什么是 dbt?面向数据工程师的上手介绍,以及我们的 dbt 入门课程。
什么是 dbt 快照?
dbt 快照是一种随时间跟踪记录变化的机制,尤其适用于那些默认不保留历史变更的表。快照主要用于实现缓慢变化维度(SCD)类型 2。当被跟踪的列发生变化时,它们会存储行的不同版本。
使用场景包括:
为何使用 dbt 快照?优势与用例
dbt 快照的价值在于,它们充当数据随时间变化的记录,使历史分析与合规报告成为可能。
优势
使用快照可带来以下一些好处:
常见用例
在一些特定情境中,快照尤为有用,例如:
架构基础
快照可与 dbt 生态体系无缝集成,借助其配置与运行管理,高效构建历史表。
以下以数据中使用的模型类型为例进行说明。
数据中主要有两类模型:
这两类模型在用途、存储方式和应用上各有不同。
以下是它们差异的概要:
功能
标准模型
快照
目的
当前状态转换
历史跟踪
存储
当前数据的表或视图
仅追加的历史表
最佳适用
事实/维度模型
缓慢变化维度
dbt 快照的工作原理
快照通常遵循一套结构化流程,确保系统性地捕获所有变更,涉及以下步骤:
dbt snapshot将当前数据与已有快照进行比较。但 dbt 是如何跟踪变更的呢?
所有变更都会记录在输出的快照表中。
如果检测到变更,dbt 会将上一版本的
dbt_valid_to字段更新为当前时间戳,并插入一行新记录,将dbt_valid_from字段设为当前时间戳。dbt 中的快照策略
快照使用两种主要策略来检测变更。
1. 时间戳策略
updated_at时间戳。2. 校验策略
策略
检测方法
最佳适用
Timestamp
更新的时间戳列
具有可靠“最后修改”时间戳的表
Check
直接比较列值
没有时间戳列的表
硬删除
诸如删除在内的数据变化也可以被跟踪,但默认不启用。不过,可以通过一种方式来监控这类硬删除变更。
invalidate_hard_deletes=True设置为真,以通过dbt_valid_to值标记被删除的行,从而保留其最后状态。下面是一个实现校验策略并考虑硬删除的示例:
dbt 快照实施指南
既然我们已经了解 dbt 快照的工作方式,下面来看如何实现它们
1. 快照教程的示例数据集
为练习快照,我们将创建一个示例 customers 表,用以模拟现实世界的会员系统。这为我们提供了一些具体数据,以测试常见的快照策略。
2. 设置您的 dbt 项目
在实现快照之前,正确设置 dbt 项目至关重要。这可确保您的环境按结构化、可扩展的方式管理模型、快照、测试与文档。
先决条件
根据您的环境,将 dbt-postgres 替换为您的仓库适配器,例如 dbt-snowflake 或 dbt-bigquery。
分步设置说明
1. 导航到目标项目目录。
这里将存放您的分析或 ELT 项目。在本例中,我们会将所有相关文件存放在此 dbt 项目文件夹中。
2. 初始化一个新的 dbt 项目。
创建一个名为 dbt_project.yml 的配置文件:
将以下内容粘贴到项目文件中:
3. 测试连接。
该命令应输出如下结果:
4. 将种子数据加载到本地数据库。
运行以下命令:
该命令应输出如下结果:
5. 配置数据库连接设置
在 profiles.yml 文件中,通常位于 ~/.dbt/profiles.yml。
以下是我的 profiles 文件内容:
如果您与我一样使用SQLite 数据库,还需要安装SQLean 扩展。
6. 将数据导入 dbt 分层(staging)模型:
创建一个名为 models 的新文件夹。在该文件夹中创建一个名为
stg_customers.sql的文件。将以下代码粘贴到文件中:
7. 在 dbt 中使用以下命令构建分层模型。
该命令应输出如下结果:
3. 配置 dbt 快照
在本例中,您可以使用以下代码执行基于时间戳策略的快照。
4. 运行 dbt 快照
执行:
该命令中,若快照表不存在,dbt 会创建之;同时会为检测到的变更插入新行。
该命令应输出如下结果:
5. 检查快照表
现在您已执行了快照,项目文件目录中应生成一个新的
snapshot.db文件。我在 VSCode 中打开它,以查看该文件的样子:
如您所见,它包含了
dbt_updated_at等附加列。这些列对于判定数据是否有变更以及变更发生的时间至关重要。如需更多 dbt 的实操指南,请查看我们的dbt 语义层指南。
示例:使用 dbt 快照跟踪数据变更
快照允许团队随时间捕获任意表中任何属性变更的完整审计轨迹,因此在合规、调试与分析方面非常强大。
customers表及相关字段。包括客户 ID、姓名、邮箱与 updated_at 时间戳,用于跟踪变更发生的时间。使用以下代码向表中插入数据。这些数据将作为变更前的原始数据。
dbt snapshot以记录初始状态。接下来,我们将通过修改一位客户的信息来更新原始表,以模拟数据变更。
dbt snapshot以捕获变更。要查看数据的差异与变更,您需要查询快照表。该表应显示邮箱地址的两个版本及其各自的有效期窗口:
此过程可确保您保留邮箱更新的完整变更历史,这对于 GDPR 审计、联系历史跟踪或客户支持分析至关重要。
高级配置与管理
有效的配置管理可让团队在项目与环境之间无缝扩展快照的实施。
1. 项目级与资源级配置
dbt_project.yml为所有快照设置默认配置(如目标 schema 或物化方式)。2. 自定义元数据字段
默认情况下,dbt 会创建
dbt_valid_from和dbt_valid_to字段。若团队标准需要不同的命名以便在 BI 或数据治理工具中更清晰,您可以通过valid_from与valid_to配置项重命名它们。3. 迁移旧版快照配置
若处理不当,迁移可能导致一些不一致。迁移快照配置时,需注意以下方面:
4. 与其他工具集成
可将数据变更的跟踪与 dbt 快照协同进行。
例如:
生产环境实施的最佳实践
在生产环境中实施 dbt 快照需要经过深思熟虑的设计,以确保性能、可维护性与业务价值。
最好遵循以下做法:
1. 最佳用例与表选择
2. 性能优化技巧
在生产中,您会希望优化数据,使变更跟踪高效且不影响产品。
以下是一些提示:
unique_key与有效期列上添加索引,以加快查询。3. 维护与监控实践
4. 常见陷阱
使用非唯一的
unique_key会导致行版本控制不正确。请在部署前始终测试唯一性逻辑。当 存储不必要的历史变更(例如频繁更新时间戳的列)时,也可能发生快照膨胀,导致存储成本快速增长。
常见挑战与解决方案
在 dbt 中使用快照可能会遇到一些特定的技术挑战:
1. 唯一键冲突
问题: 由于唯一键定义不当,导致快照中出现重复行。
解决方案:
2. 时态查询性能
问题: 在分析历史数据时查询缓慢。
解决方案:
dbt_valid_from或日期范围过滤查询,以减少扫描数据量。3. 排查快照执行错误
常见问题包括:
SELECT语句报错。解决方案:
dbt debug验证连接与配置。结论
dbt 快照使分析团队能够以最小的工程投入构建稳健的历史数据集,支持合规分析。若您有兴趣进一步了解 dbt,欢迎查看我们的dbt 入门课程。若需阅读材料与更多指南,请查看我们的dbt-utils 包入门与dbt 教程。