从零开始的 dbt 入门教程 (dbt-core 基础篇)

1,893 阅读21分钟

在最近一段时间,我一直在处理数据开发相关的工作,谈到数据开发,我们不得不聊聊目前非常流行的开源数据转换工具 dbt,它也在 ETL 中扮演着非常重要的一环。

我作为数据开发的新手,在这段时间也积累了不少经验,考虑到目前很少有人会详细记录 dbt 相关的文档,因此我想通过几篇系列文档来分享我在开发中积累的心得,这个系列会非常适合数据开发初学者,我也非常希望这些文档会对作为读者的你有所帮助。

本篇文章作为 dbt 系列文章的开篇,通过阅读本文,我希望向你传达如下信息:

  • 一些作为数据开发、数据分析初学者需要提前了解的概念
  • dbt 是什么?带你了解 dbt 一些比较重要的概念
  • 使用 dbt 的环境准备
  • 如何安装 dbt core 以及 dbt adapters
  • 如何使用 dbt adapters 连接数据库
  • 使用 dbt 创建属于你的第一个模型

那么让我们带着问题开始。

一、数据开发可能需要知晓的前置概念

现在,让我们先了解作为数据开发新手可能需要知晓的一些前置概念。

1.1 什么是 dbt?

DBT(Data Build Tools)是一种数据转换工作流工具,作为数据开发师,我们需要将原始数据进行各类加工以及聚合来应对更为复杂的数据分析需求。准确来说,dbt 就是一个辅助你写各类数据查询 sql 的数据转换工具。

你可能会想,如果只是写 sql 那我为什么还需要使用 dbt 呢。事实上,由于数据库种类繁多,不同数据库对于 sql 语句都会存在兼容问题;除此之外,数据转换会存在数据依赖,比如底层原始数据层层转变为业务数据,你可能需要自己来维护这个依赖关系,你也许确实能做到 dbt 的很多能力,但仔细一想,你就会发现这些细节处理都会显得格外麻烦,为什么不使用一个工具帮你去处理这些基础的事情呢?

为了更好的理解 dbt,我们可以把 dbt 想象成一个厨师,而原始数据就像是食材。厨师(DBT)的工作就是把食材(原始数据)加工成好吃的食物(能提供业务价值的数据模型),这个过程就像是烹饪一样,它得按照一定的顺序和步骤来操作。

dbt 的工作流程就像是烹饪的步骤,我们只需要告诉 dbt 想做什么菜(你想要的数据模型是什么样的,并定义好 sql ),然后 dbt 会按照你的指示,一步步地处理食材(即处理原始数据),最后做出你想要的食物(即生成你需要的数据模型)。

其实说到这,你就能明白 dbt 能让我们更聚焦在我们想要什么数据,定义怎样的数据模型上,其余的工作流我们全权交给 dbt 即可,这就是 dbt 的作用。再说的遥远一点,dbt cloud 还提供了自动化部署以及自动的定时任务来帮你构建模型,当然这些是我们后续文章需要细聊的部分了。

1.2  dbt core 和 dbt cloud  是什么?

如果你打算阅读 dbt 的文档,你一定会留意到 dbt core 与 dbt cloud 两个名词,这两者的作用于和区别是什么呢?让我们先来了解:

  • dbt core:dbt Coredbt 的开源部分,它提供了数据建模、转换和管理的核心功能。使用 dbt Core,你可以定义和运行数据转换模型,生成 SQL 查询,并将数据写入目标数据仓库(如BigQuery、Snowflake等)。总而言之,你接下来要在控制台所使用的 dbt 命令都是基于 dbt core,因此 dbt core 必须安装。
  • dbt Cloud: dbt Clouddbt 的云服务,构建在 dbt Core 的基础之上。它提供了托管服务,CI/CD 部署以及图形化的用户界面,能让你直接在平台运行 dbt 模型而无需自己设置和搭建基础设施,在后续文档我会单独聊 dbt cloud,教大家如何通过自动化来做到项目部署以及模型的定时更新。

我们可以先了解 dbt core,在后续文章中我也会介绍如何使用 dbt cloud 做数据开发。

1.3 什么是 dbt adapters?

如果我们使用 dbt core 做数据开发,那自然得想办法让本地的 dbt 与远端的数据库建立连接,因此除了 dbt core,第二个非常重要的概念就是 dbt adapters;事实上,如果我们使用 dbt core 就一定得使用你希望连接数据库所对应的 dbt adapters。

大家都知道不同的数据库在 SQL 查询上都会有些许差异,要记住所有数据库独有的语法成本高但收益低,而 dbt adapters 正好帮我们做了这件事。dbt adapters 作用之一是提供一种标准化的接口,让你可以在项目中安心写 SQL 语句而不需要过分考虑数据库兼容问题。

dbt 提供的 adapters 非常多,除了 dbt 官网维护的适配器之外,还有社区自行维护且受 dbt 官方认可的 adapters,所以从数据平台(比如 bigQuery、Postgres)到数据库(比如 mysql )本身。
dbt 为所有的 adapters 都提供了独立的文档以及详细配置说明,大家根据自己需要连接的数据库类型来选择对应的 adapters,具体的文档可阅读 about-core-connections

1.4 什么是 ELT 和 ETL ,它们区别在哪?

ELT 和 ETL 是两种常见的数据分析模式,它们在数据处理流程中的步骤顺序上有所不同。

  1. ETL:
    • 提取(Extract): 从源系统中提取数据。
    • 转换(Transform): 对提取的数据进行清洗(比如去除空值)、加工、转换。
    • 加载(Load): 将经过转换的数据加载到目标系统,通常是数据仓库。

在 ETL 模式中,数据在提取后经过一系列复杂的转换操作,然后再加载到目标系统。这种模式适用于需要对数据进行多次、复杂转换的情况,比如将多个源的数据合并,进行聚合等。

  1. ELT(提取、加载、转换):
    顾名思义,在数据处理顺序上有所不同。ELT 模式中,数据首先加载到目标系统,然后在目标系统内进行转换。这种模式适用于目标系统有足够计算资源的情况,可以在目标系统中直接处理原始数据。

两者区别:

  • ETL 的优势: ETL 适用于需要在数据到达目标系统前进行复杂的数据清洗和转换的情况。它可以将清洗和转换的逻辑分离出来,确保目标系统中的数据是高质量的。
  • ELT 的优势: ELT 更适用于云数据仓库等具有强大计算能力的系统( 比如 bigQuery )。它允许直接在目标系统中处理原始数据,减少了数据传输的复杂性,适用于大规模数据处理。

1.5 Dbt ,bigQuery 与 Fivetran 的作用

理解 ELT(Extract, Load, Transform)和 ETL(Extract, Transform, Load)的概念有助于更好地理解这三个工具在数据处理和分析中的角色。

  1. Fivetran:
    • ETL角色定位: Fivetran 主要负责从各种数据源提取(Extract)数据,并将这些数据加载(Load)到目标数据仓库,如 BigQuery。
    • 作用: Fivetran 简化了数据提取和加载的过程,使数据准备的阶段更加快速和无缝,除此之外,Fivetran 还会做部分数据预处理工作,大致能力:
      1. 数据格式转换: Fivetran 可以处理来自不同数据源的数据,并将其转换为适合目标数据仓库的格式。这可能涉及日期格式、数字格式等的调整。
      2. Schema映射: Fivetran 会根据目标数据仓库的结构映射,将数据源的表和字段映射到目标仓库中的对应结构。
      3. 增量同步: Fivetran 通常支持增量同步,只同步源数据中发生变化的部分,以减少数据传输的成本。
      4. 错误处理: 处理在数据加载过程中可能出现的错误,确保数据的完整性。
      5. 性能优化: 优化数据加载的性能,以确保数据能够及时可用。
  1. BigQuery:
    • ETL角色定位: BigQuery 在 ELT 流程中扮演 Load 阶段的角色。它是一个云数据仓库,负责存储和处理加载进来的原始数据。
    • 作用: BigQuery 提供强大的分布式查询引擎,允许用户在原始数据上执行复杂的 SQL 查询,进行初步的数据分析。
  1. dbt:
    • ETL角色定位: dbt 既可以在 ELT 模式下使用,也可以在 ETL 模式下使用,取决于具体的架构设计。在 ELT 中,dbt 用于数据转换和建模,通常在加载后的原始数据上执行。在 ETL 中,dbt 可以与其他 ETL 工具配合使用,用于定义和执行更复杂的数据转换逻辑。
    • 作用: dbt 的主要作用是定义和执行数据模型,提供了一种可维护、可测试的方法来构建和管理分析模型。

整体流程:

  1. ELT流程:
    • Extract(Fivetran): 从各种数据源提取数据。
    • Load(Fivetran和BigQuery): Fivetran 将数据加载到 BigQuery 中。
    • Transform(dbt): 使用 dbt 在 BigQuery 中创建和维护分析模型。
  1. ETL流程:
    • Extract(Fivetran): 从各种数据源提取数据。
    • Transform(dbt等工具): 使用 dbt 或其他 ETL 工具定义和执行数据转换逻辑。
    • Load(BigQuery): 将转换后的数据加载到 BigQuery 或其他数据存储中。

这种结合 ELT 和 ETL 的方式,利用了 Fivetran 的强大数据加载能力,同时通过 dbt 提供的数据建模工具,实现了灵活而可维护的数据处理和分析流程。

1.6 dbt 负责的数据转换,而 Fivetran 也能做数据转换,那为什么还需要dbt?

  1. Fivetran: Fivetran 主要专注于数据集成,即将数据从不同的源头传输到目标数据仓库。它强调的是数据的可靠、高效的移动。虽然 Fivetran 提供了一些基本的预处理功能,但它并不是一个专门用于复杂数据转换和业务逻辑的工具。它的目标是提供一个易于使用的平台,使得数据工程师可以快速地设置和管理数据流。
  2. dbt: dbt(data build tool)则专注于数据转换和建模。它在数据仓库中执行转换和汇总,以便为分析提供更具可读性和易用性的数据结构。dbt 允许分析师定义业务逻辑、创建衍生字段、执行聚合等操作,将原始的仓库数据转化为更容易理解和使用的形式。dbt 的强项在于支持分析人员更好地理解和使用数据,而不仅仅是数据的传输和存储。

综合考虑,Fivetran 和 dbt 可以协同工作。Fivetran 负责将数据从源头搬移到数据仓库,而 dbt 则负责在数据仓库中进行进一步的处理和建模,以便更轻松地进行复杂的查询和分析。简单理解,Fivetran 只提供了基础的数据清洗和转换,而 dbt 提供更专业更强大更自由的数据转换。

二、 dbt 环境准备(这里以 Python 为例)

聊完前置概念,现在让我们正式开始接触 dbt,我们先从环境配置开始。

2.1 Python 版本注意

与 npm 需要依赖 node 一样,pip 命令也需要安装 Python,关于版本这里推荐安装 3.8   3.9 即可,不要安装 3.10。我在安装了 Python 3.10 后出现了安装 mysql 适配器和 core 包时,一直只能安装版本为 0.19.2 的情况,而 core 最新的版本都到了1.1.6,这导致我一直陷入了包版本依赖错误的困境中。

2.2 安装 dbt core

上文已经提到 dbt core 属于 dbt 的开源核心,我们后续使用的命令都由这个包提供。安装 dbt core 的方式有很多,官方支持 pip、docker、homebrew 等等。

上文我们已经安装了 Python ,所以我们在终端执行如下命令即可:

pip install dbt-core

dbt 默认全局安装,所以即便你在某个项目路径下,它还是会基于全局安装,在安装完成之后,我们能执行如下命令检查安装是否完成。

pip show dbt-core

比如我安装的就是 core 1.1.6 版本,这里就能看到安装的版本,路径等相关信息。

2.3 安装 dbt adapters

我们后续所有工作,都将基于 core 与 adapters 两个包来完成,其实准备来说,当我们执行安装某个 adapter 时,这个命令会默认安装与之关联的 core 包,也就是一个命令自动安装两个包,这里我们以 bigQuery 为例:

pip install dbt-bigquery

同理,安装之后可以执行命令检查安装包的版本等信息:

pip show dbt-bigquery

某些情况下,你先执行了下载适配器的命令,会默认帮你 core 包,你也许想单独再安装更高版本的 core 包,你可以通过卸载重装的形式来完成,比如:

## 先卸载
pip uninstall dbt-core
## 再安装执行版本包
pip install dbt-core==1.1.6

以上就是 dbt 两个核心的包了,我们只需要安装这两个包就能支撑接下来的所有工作,再做个总结,解释下两个包的作用:

  • dbt-core:开源的核心包,安装了这个你才能执行 dbt 命令。
  • 适配器包(dbt-bigquery):数据库平台、数据库兼容的包,帮你抹平不同数据库的命令差异,而且接下来我们连接对应的数据库,都需要提前安装对应的适配器。

关于适配器的对于不同数据库的兼容情况,详细信息可查看官网说明:

四、初始化 dbt 项目

4.1 通过命令初始化

可能到这里,大家觉得安装完 adapter 以及 dbt core 就已经够了,其实除了 dbt 外,我们还需要准备一个 dbt 项目,这个项目就是前文提到的食材和菜谱,你得在这个项目里定义你的数据模型,这样我们在项目终端运行 dbt 命令,dbt 就能帮我们按步骤来做菜了。

初始化一个 dbt 项目有两种方式,第一种是直接通过命令:

## 你可以在你的项目目录下运行
dbt init

之后 dbt 会帮你创建一个模版项目,假设你出现了 dbt 命令不存在的报错,那么就是你的 dbt core 包没安装好,你应该重新执行上文提到的 dbt core 安装命令,以及通过 pip show dbt-core 来检查你的 core 版本信息。

需要注意的是,我第一次使用 dbt init 初始化项目,这个命令很奇怪会在电脑根路径创建一个profiles.yml配置,而项目本身就是依赖这个配置来与数据库建立联系。

其实大家应该都想得到,这种核心配置肯定得跟着项目走,全局即便有也应该会被项目内的配置所覆盖,结果我在项目根目录专门创建profiles.yml后执行命令每次还是走根路径的配置,此问题可能跟我 dbt 版本有关,暂时解释不了。

我查阅了官网的说明,官网也确定会优先走项目根目录的配置,电脑根目录配置只是起到默认配置的作用。

4.2 使用官网模版项目

除了命令,我更推荐使用官网的 dbt 模版项目,因为 dbt 命令除了数据做转化,还包含源数据写入数据库的命令,而 init 创建的模版只是最基础的模版,不包含模拟数据,也不包含模拟的数据模型,一切从零开始还是会有一定上手难度。

因此我建议大家使用官方模版项目 jaffle_shop,跳转 GitHub 然后使用 git clone下载本地,之前由于我们已经安装了 core 和对应的适配器,所以我们直接拿这个项目练手就好了。

使用 jaffle_shop 项目另一个好处是,由于缺少 init 命令过程,所以不会出现上面提到根目录创建配置导致无法走项目配置的问题。

五、连接数据库

现在我们准备了所有环境,让我们尝试连接数据库,这里我们以 bigquery 为例,因此我将使用 bigquery adapter。

首先 bigquery 是 Google 的数据平台,它支持四种连接方式,具体的连接方式和配置可参考:bigquery setup。这里我以 Service Account File 的方式来连接。

5.1 创建 profiles.yml 文件

我们需要在之前 clone 的 jaffle_shop 项目根目录创建 profiles.yml 文件,之后粘贴如下代码到文件即可:

jaffle_shop:
  target: dev
  outputs:
    dev:
      type: bigquery
      method: service-account
      project: demo-data-analytics
      dataset: dev_data_statistics
      threads: 4 # Must be a value of 1 or greater
      keyfile: data-analytics-bef7505.json

我们来解释下配置:

首先 jaffle_shop 这个字段需要跟我们项目根目录下 dbt_project.yml 中的 profile 属性字段相同,你可以取任何名字,但两边得保持一致。

target: dbt 项目本身也有分支和环境的区别,现在我们处于学习阶段,所以这里环境可以定义为 dev 即可。

type:你所使用适配器所对应的数据库名称,因为我们使用的是 bigquery,所以这里填 bigquery 即可。

method: 因为我们选择了 Service Account File 方式连接 bigquery,所以这里填写 service-account 即可。

project: 数据库最外层的容器名称。

dataset: 数据集名称,我们创建的模型都会写入到此数据集。

threads:用于指定运行 dbt 任务时的并发线程数。使用多个线程,可以加快 dbt 流水线的执行速度,这里我们也默认为 4。

keyfile:连接 Google 数据库也需要授权,简单理解就是一份秘钥,因为我的秘钥也直接放在了项目根路径,所以我这里直接引即可。如果你也是使用 bigquery,关于创建服务账号可参考文档

5.3 检查连接是否成功

配置完成后,我们执行 dbt debug 可以检查项目与数据库的连接情况,比如:

到这里,我们成功让 dbt 项目连接到了 bigquery(如果出现错误,请根据错误信息检查上文提到的配置是否正常)。

我们要做数据开发或者数据分析,前提一定是数据库中已经存在部分数据,而 jaffle_shop 提前为我们准备好了源数据文件和模型文件,现在让我们执行如下命令:

## 将项目中 scv 数据文件写入连接的数据库
dbt seed

## 运行整个 dbt 项目,开始数据建模
dbt run

在执行完成后,当我们来到 bigquery 找到 dev_data_statistics 数据集,在此数据集下即可看到 jaffle_shop 中所定义的模型文件,比如

有一个小心得,dbt 在数据库中创建的所有模型名称都默认是我们的 sql 文件名,这也利于我们对应查找 dbt 所创建的表或者视图。

那么到这里我们基本走完了一个 dbt 流程,成功配置且运行了属于我们第一批数据模型。

六、dbt 命令注解

文章最后,我顺带整理了部分 dbt 命令注解,希望对你有所帮助:

  • build:按照指定的顺序运行所有的数据加载、数据模型、数据快照和数据测试。这个命令会编译 SQL 并执行相应的操作,构建数据仓库。
    • build 包含了 seed 、run、test、snapshot,也就是把原始数据加入数据库,基于模型生成视图,测试用例以及生成快照。
  • clean:删除指定的文件夹,通常用于清理生成的文件或目录。
  • clone:创建一个节点的副本,可以在项目中复制和重用节点。这个命令可以帮助你快速创建类似的数据模型。
  • compile:将 dbt 项目中的代码转换为可执行的 SQL 语句,这个命令可以帮助你检查和验证你的代码是否正确。
  • debug:显示当前 dbt 环境和配置的信息(上面用过了),这个命令可以帮助你了解当前的 dbt 设置和环境变量。
  • deps:更新项目中使用的依赖项,以获取最新版本的依赖库(dbt 也有三方包,后续文章讲)。
  • docs:生成或提供你的项目的文档网站,这个命令可以帮助你生成和查看项目的文档,以便其他人了解你的数据模型和操作。
  • init:初始化一个新的 dbt 项目,这个命令会创建一个新的 dbt 项目,并生成必要的文件和目录结构,以便你开始构建数据仓库。
  • list:列出项目中的资源,如数据模型、表、视图等。这个命令可以帮助你查看项目中的所有资源。
  • parse:解析项目并提供关于性能的信息,这个命令可以帮助你了解项目的结构和性能,以便进行优化。
  • retry:重新运行上次运行失败的节点,这个命令可以帮助你重新运行失败的数据模型或操作,以解决错误。
  • run:编译 SQL 并运行指定的数据模型或操作。这个命令用于执行 dbt 项目中的数据模型和操作,以构建数据仓库。
    • seed 和 run 的区别:
      • seed 的主要目的是加载原始、静态的数据,这些数据通常不需要经常变动,例如国家列表、产品类别等。seed 会负责将这些静态数据加载到数据库中,为后续的分析和转换提供基础数据。
      • run 的主要目的是运行数据模型,通过执行 SQL 查询和转换逻辑,生成新的表、视图或者其他的数据结构。这些模型可能依赖于 seed 导入的数据,也可能依赖于其他模型生成的数据。
    • 有时候我们只想运行某个 model 而不是所有 models ,通过 --model 可以执行运行某个 model,比如dbt run --models model_name,或者dbt run --models model1,model2
    • 有时候我们希望通过命令区分环境,比如 dbt run --target dev 或者 prod
  • run-operation:运行指定的宏(macro),并传递任何提供的参数。这个命令可以帮助你运行自定义的宏,以实现特定的数据处理逻辑。
  • seed:从 CSV 文件中加载数据到数据仓库中。这个命令用于将数据加载到你的数据仓库中,以供后续的数据模型使用。
  • show:为指定的数据模型或操作生成可执行的 SQL。这个命令可以帮助你查看指定数据模型或操作的 SQL 代码。
  • snapshot:执行项目中定义的数据快照操作。这个命令用于执行 dbt 项目中定义的数据快照操作,以捕捉数据的历史状态。
  • source:管理项目的数据源。这个命令可以帮助你添加、配置和管理项目中的数据源,以便从不同的数据源中提取数据。
  • test:这个命令用于在已部署的数据模型中运行数据测试,以确保数据的准确性和一致性,基本作用:
    • 验证数据的准确性、完整性和一致性。
    • 验证数据转换逻辑是否正确。
    • 验证数据之间的关系和约束。

举个 test 的例子:

models:
  - name: my_model
    tests:
      - my_test:
          severity: error
          description: "Check if column X contains null values"
          check: "select count(*) from {{ ref('my_model') }} where X is null"
          expect: "select 0"

在上面的示例中,my_test 是一个测试用例的名称,my_model 是模型的名称。check 查询语句中使用了 {{ ref('my_model') }} 来引用模型,然后检查模型中的列 X 是否包含空值。

那么在下篇文章,我们来学习 dbt core 进阶的用法,比如如何区分测试和生产环境,将同一份模型写入到不同环境所期望的数据集,了解更高阶的 dbt 命令以及更详细的 dbt 配置,敬请期待。