增量模型用于构建数据仓库表。dbt第一次运行模型时,构建所有源数据的行。随后的运行中,只转换筛选出的行,并将它们插入到已构建的表中。增量运行中,筛选出的每行数据基于自上次dbt运行以来创建或更新的行。每次运行dbt时,都会以增量方式构建模型,从而减少转换时间,提高仓库性能并降低计算成本。
需要注意一点的是,目前dbt还暂不支持在hive中进行增量模型的数据刷新。
配置增量模型
dbt中增量模型是用select语句的配置代码块中定义。
{{
config(
materialized='incremental'
)
}}
select ...
要使用增量模型,还需要告诉dbt:
- 如何在增量运行中筛选行
- 模型的唯一键(如果有)
查询增量数据
要告诉dbt应该在增量运行中转换哪些行,需要在is_incremental()宏中包装筛选这些行的有效SQL。
通常,筛选增量数据,最佳方法是检查目标表中的数据更新时间戳。使用dbt的“{{this}}”变量,我们可以轻松地查询目标表。
同样想要捕获新的和更新的记录,需要定义一个唯一键,以确保不会将修改后的记录作为重复项引入。is_incremental()代码将检查自上次dbt运行此模型以来创建或修改的行。
对于数据量比较大的数据转换,可以选择增量模型构建,例如下面例子:
{{
config(
materialized='incremental'
)
}}
select
*,
my_slow_function(my_column)
from raw_app_data.events
{% if is_incremental() %}
--该过滤条件最会在增量运行时起作用
where event_time > (select max(event_time) from {{ this }})
{% endif %}
定义唯一键(可选)
unique_key可以用来更新现有行,而不仅仅是添加新行。如果有一个新的记录具有相同的unique_key,那么它可以替换当前的记录,而不是被追加到表中。如果有重复的记录被筛选出来,那么可以忽略它们。
如果没有指定unique_key,dbt会将模型SQL返回的所有行追加到目标表中,而无论这些行是否已经存在于目标表中。因此可能会出现重复数据的情况。为了避免这种情况,应该指定一个unique_key来确保只有唯一的行被插入到目标表中。
unique_key参数用于指定模型粒度的字段,也就是用于标识单个唯一行的字段(或字段组合)。可以在模型顶部的配置块中定义unique_key,它可以是单个列名或列名列表。
在模型定义中,应该使用unique_key来表示单列的字符串或可一起使用的单引号列名列表,例如['col1','col2',…]。这些列不应包含任何null,否则增量模型运行可能会失败。为了确保每一列都没有null,可以使用coalize(column_NAME,'VALUE_IF_NULL')函数,或者定义单列代理项键,例如使用
dbt_utils.generate_surrogate_key函数生成唯一键值。
当定义unique_key时,将看到dbt模型返回的每一行“更新”数据的效果,如下:
- 如果新旧模型数据中存在相同的unique_key,dbt会使用新行数据来更新或替换旧行。更新或替换的机制取决于dbt运行的数据库、增量策略和特定于策略的配置。
- 如果unique_key不存在于“旧”数据中,dbt将把整行插入到表中。
唯一键例子
下面是一个基于事件流计算每日活动用户(DAU)数量的模型。当源数据到达时,将需要重新计算上次运行dbt的日期以及此后任何一天的DAU数量。该模型如下所示:
{{
config(
materialized='incremental',
unique_key='date_day'
)
}}
select
date_trunc('day', event_at) as date_day,
count(distinct user_id) as daily_active_users
from raw_app_data.events
{% if is_incremental() %}
--该过滤条件最会在增量运行时起作用
where date_day >= (select max(date_day) from {{ this }})
{% endif %}
group by 1
如果不使用unique_key参数,每天运行一次dbt会在目标表中增加一行,导致出现多行。使用unique_key参数可以确保更新现有行,避免重复数据。
增量模型的重建
如果增量模型的逻辑发生了变化,那么新数据行的转换可能与之前存储在目标表中的转换逻辑不同。因此,在这种情况下,建议重新构建增量模型以确保数据的准确性。
如果想从头开始重建整个增量模型,请在命令行中使用--full-refresh标志。这将导致dbt重新生成数据库中的现有目标表,并删除之前的历史数据。