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

最近一直在处理数据分析和数据建模的事情,所以接触了 dbt 等数据分析的工具,国内目前对于 dbt 比较详细的资料不多,所以打算写四道五篇 dbt 相关的文章,本文属于 dbt 系列的第一篇,本篇主要阐述 dbt 一些基本概念,教会你如何配置 dbt 连接远端数据库,并运行你的第一个数据模型,那么本文开始。

一、一些数据分析可能需要知晓的前置概念

1.1 什么是 dbt?

DBT(Data Build Tools)是一种数据转换工作流工具,作为数据分析师,我们需要将原始的数据进行各类加工组合来应对更为复杂的数据分析需求。准确来说,dbt 还是来写 sql 做各种数据查询,你可能会想,那我自己写 sql 不就完事了,还用啥 dbt。事实上,由于数据库种类繁多,sql 会存在兼容问题;其次,数据转换会存在数据依赖,比如底层原始数据层层转变为业务数据,你可能需要自己来维护这个依赖关系,其实仔细一想,你就会发现自己做会非常麻烦。

我们可以把 DBT 想象成一个厨师,而原始数据就像是食材。厨师(DBT)的工作就是把食材(原始数据)加工成好吃的菜(有用的数据模型)。这个过程就像是烹饪一样,需要按照一定的顺序和步骤来操作。

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

其实说到这,你就能明白 dbt 能让我们更聚焦在我们想要什么数据,定义怎样的数据模型,其余的工作流全权交给 dbt 即可,这就是 dbt 的作用。

1.2 dbt core 与 dbt cloud 的区别

我们在 dbt 文档开头会留意到两个比较重要的名词,dbt core 和 dbt cloud,新手可能已经不知道该从哪个入手了,这里先解释下区别:

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

总而言之,就是 saas 付费服务和开源本地化自行搭建的区别,那么本文自然是从 dbt core 的视角出发了。

1.3 什么是 dbt adapters?

除了 dbt core,第二个重要的概念是 dbt adapters,也就是 dbt 适配器,而且我们要做数据处理一定是安装 dbt core + 某个数据库所对应的适配器。

大家都知道不同的数据库在 SQL 查询上都会有些许差异,要记住所有类型的特定语法成本高但收益低,适配器正好帮我们做了这件事,适配器的作用之一是提供一种标准化的接口,让你可以使用相同的 SQL 语法来与不同的底层数据平台交互,而不需要关注 SQL 语句本身。

dbt 本身提供的适配器就非常多,除了官网维护的适配器之外,还有社区自行维护且受 dbt 官方认可的适配器,所以从数据平台(比如 bigQuery、Postgres)到数据库(比如 mysql)本身。
dbt 为所有的适配器都提供了独立的文档,以及配置说明,大家根据自己的数据库类型可以直接来这个文档目录搜索查看即可。

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

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

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

  1. ETL:

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

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

  2. ELT(提取、加载、转换):

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

两者区别:

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

1.5 Dbt ,bigQuery 与 Fivetran 的作用

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

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

整体流程:

  1. ELT流程:
    • Extract(Fivetran): 从各种数据源提取数据。
    • Load(Fivetran和BigQuery): Fivetran 将数据加载到 BigQuery 中。
    • Transform(dbt): 使用 dbt 在 BigQuery 中创建和维护分析模型。
  2. 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 为例)

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,导致我一直陷入了包版本是对的,但是 core 与 mysql 适配器依赖包版本错误需要解决版本冲突的怪圈中,这点切记。

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 版本,这里就能看到安装的版本,路径等相关信息。

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

2.3 安装 dbt adapters

我们后续所有工作,都将基于 core 与 adapters 两个包来完成,其实准备来说,当我们执行安装某个适配器时,这个命令会默认安装与之关联的 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 通过命令

可能到这里,大家觉得安装完适配器以及dbt core 就已经够了,然后我们在终端直接通过命令链接数据库进行操作,其实不是的。我们需要初始化一个项目,而且我们还需要在项目中编写数据模型语句,以及一些基本的配置代码。

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

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

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

4.2 使用官网模版项目

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

大家可以直接跳转 jaffle_shop,然后 git clone下载本地,之前由于我们已经安装了 core 和对应的适配器,所以我们直接拿这个项目练手就好了。

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

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

我查阅了官网的说明,官网也确定会优先走项目根目录的配置,电脑根目录配置只是起到默认兜底的作用。但假设大家使用 jaffle_shop 项目,因为缺少 init 过程,没有根目录创建配置的行为,起码后续我的命令确实走了项目内的配置。

五、链接数据库

万事俱备,现在我们需要链接数据库,接下来才能做数据转换的工作,这里我们以 bigquery 为例。

首先 bigquery 是 Google 的数据平台,它支持四种连接方式,具体可参考:bigquery setup

这里我以 Service Account File 的方式来链接。其次,由于 Google 数据库不支持本地连接,所以我们需要把 clone 的项目整个上传服务器,通过远端来联调数据库,前端同学如果不清楚,可以问下服务端同学日常开发怎么做的就好了。

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

5.2 解释下配置

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

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

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

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

method:固定配置,不用改。

project:项目名,注意是你的项目名,当然也可能叫数据容器名,就是最外层的容器名。

dataset:数据集名称,一般一个数据集下包含多张数据表,所以这个关系就是数据库---数据集--数据表的关系。但需要注意的是,一般数据库有N个数据集,我们操作数据也不可能只操作一个数据集,所以这里的 dataset 只是作为默认值,如果大家有数据库所有权限,还是能查询所有数据集,不过它会影响 dbt seed 数据库新表的创建位置,一般我们 dataset 提供哪,seed 命令创建的新表就在哪。总而言之,不影响查询数据转换,但是影响 seed 和我们 model 新表、视图创建的位置。

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

keyfile:链接 Google 数据库也需要授权,简单理解就是一份秘钥,因为我的秘钥也直接放在了项目根路径,所以我这里直接引即可。

5.3 检查链接是否成功

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

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

那么到这里,我们成功让 dbt 项目链接到了远端数据库,要做数据分析或者建模,我们肯定得提前往数据库写入部分数据然后再基于数据做分析,使用上文提到的 jaffle_shop 模版项目的好处就是,项目直接为我们准备好了数据文件和模型文件,现在让我们执行如下命令:

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

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

在执行完成后,当我们来到数据库中的 dev_data_statistics 数据集,在此数据集下即可看到 jaffle_shop 中所定义的模型文件。有一个小技巧,dbt 在数据库中创建的所有模型名称都默认是我们的 sql 文件名,这非常方便我们对应查找表或者视图。

那么道理,我们基本走完了一个 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 视图(view)和表(table)的区别

dbt run命令默认创建的是视图(views),但是你可以通过在模型文件中设置{{ config(materialized='table') }}来指定dbt run生成表(tables)而不是视图,或者在 dbt_ project 配置中对文件夹进行定义。

举个例子,我们在 dbt 模型文件顶部添加以下代码:

{{ config(materialized='table', sort='timestamp', dist='user_id') }}

-- Your SQL code here

这样,当我们运行dbt run命令时,dbt 会创建一个表而不是一个视图。

关于视图和表的区别,这里我给一些比较生硬但明显的区别:

  • 视图(view):视图的主要作用是提供一个虚拟的表,它是基于一个或多个表的查询结果而创建的。视图可以看作是一个预定义的查询,它将查询逻辑封装在其中,并提供一个简化和抽象的接口来访问和操作数据,注意,视图本身不存储数据。视图在以下情况下很有用:
    • 视图是一个虚拟的表,它是基于一个或多个表的查询结果而创建的,你可以使用视图来对原始数据进行聚合、计算和转换,以生成更高层次的指标和洞察。(视图只读,不能增删改)
    • 视图不存储实际的数据记录,而是根据查询逻辑动态生成结果。
    • 视图可以看作是一个预定义的查询,它将查询逻辑封装在其中,并提供一个简化和抽象的接口来访问和操作数据。
    • 视图可以简化复杂的查询操作,提高查询的效率和可读性。
    • 视图可以用来限制对数据的访问权限,只暴露需要的数据给用户,视图可以用于限制用户对敏感数据的访问。通过在视图中应用过滤器、权限和安全规则,你可以控制用户对数据的访问权限,确保只有授权的用户可以查看和使用特定的数据。
  • 表格(table):这会在你的数据仓库中创建一个实体表,并在每次dbt run时重新填充。表是数据库中存储数据的主要结构。与视图不同,表会存储数据。当你查询表时,你会直接获取存储在表中的数据,而不需要重新计算或检索数据。表的一些特点:
    • 表是数据库中的实际存储对象,它包含了实际的数据记录。
    • 表可以存储和管理大量的数据,可以进行增删改查等操作。
    • 表具有物理结构,包括列和行,每一行代表一个数据记录,每一列代表一个属性。
    • 表的数据是实时更新的,当对表进行增删改操作时,数据会直接在表中进行修改。

总结起来,视图和表在用途上可能有很多相似之处,但表是实打实的数据,而视图是一个中间态的数据,前者可以CRUD直接操作,而视图只能查,像修改只能 dbt run 完整更新。

那么到这里,第一篇文章结束,第二篇我们详细聊下 dbt 配置,以及一些更进阶的用法,比如区分 dev prod 将模型写入不同目标数据集等等,关于本文有任何走不通的地方或者疑问环境留言提问。

12-01 06:44