本节摘要:dbt 的机体由三部分组成:Core 是本地的编译器与执行器,负责把模板 SQL 变成可执行语句并驱动仓库干活;适配器是连接不同数仓的翻译层,让同一套项目代码能跑在几十种平台上;Cloud 是托管版,把调度、环境、文档与权限搬到了网页里。理解三者的分工,你就能明白「一条命令背后发生了什么」,也能在选型时判断免费版够不够用。
把 dbt 比作一台施工设备,三个部件的分工可以这样记:Core 是发动机,适配器是不同工地的转接头,Cloud 是带调度的中央控制室。
Core 是开源的命令行程序,装在本地或你的服务器上。所有核心命令——编译、运行、测试、生成文档——都是它发出的。它读入你的项目文件(模型、配置、测试、宏),在本地完成编译,把产物 SQL 通过数据库连接发给仓库执行,再把结果与日志汇报给你。它自己不存任何数据,也不带调度能力:你让它跑它才跑,什么时候跑由 cron、Airflow 或者 dbt Cloud 决定。
适配器(adapter) 解决的是「仓库方言不一致」的问题。Snowflake、BigQuery、Redshift、PostgreSQL、Databricks,各家仓库的 SQL 方言、连接方式、权限模型都不一样。dbt 的做法是把「生成 SQL、管理依赖、跑测试」这些通用逻辑留在 Core,把「怎么连接、怎么建表建视图、怎么处理事务」这些平台相关的逻辑抽到适配器里。装哪个适配器,dbt 就会说哪家的「方言」。这套设计和数据库驱动、浏览器内核的适配器模式一脉相承:上层协议统一,底层实现各异。
dbt Cloud 是官方的托管平台,把开发者要在自己机器上搭的一整套东西搬到了网页:网页里的开发编辑器、定时调度、环境管理、执行历史、文档托管、权限与审计。Core 加自建调度可以搭出功能等价的组合,代价是自己维护;Cloud 用订阅费换运维成本。两者的项目代码完全兼容——用 Core 开发的项目可以直接放上 Cloud,反过来也一样。
以最常用的构建命令为例,从敲下回车到数据落库,中间经过这几步:
这套流程里,1、2 属于装配,3、4 属于 Core 的编译期,5 属于执行期,6 是观测。后面章节的很多机制都落在这几步上:物化策略影响第 5 步的执行方式,宏可以介入第 4 步的编译过程,性能调优主要优化第 3、5 步。
讲一个能体现这套架构价值的场景。某公司把分析仓库从自建 PostgreSQL 迁到云上的 BigQuery,数据团队担心几十个模型全部重写。实际情况是:模型的 SQL 主体(SELECT 语句)基本原样迁移,需要调整的只有三类零碎之处——个别方言函数(日期截断函数两家写法不同)、物化配置(把为 PostgreSQL 调的参数换成 BigQuery 的分区与聚簇配置)、以及连接配置里的项目与数据集参数。三天内完成迁移,期间旧环境还并行跑了一周做数字比对。
之所以能这么轻,是因为「这个模型依赖哪些上游、按什么顺序跑、测试怎么验」这些信息全都存在项目代码里,与具体仓库无关;换仓库换的只是适配器这层「转接头」。反过来,这个案例也有另一面:适配器屏蔽了连接与物化的差异,但屏蔽不了算力与成本模型差异。 同样一条 SQL,在 PostgreSQL 上全表扫无所谓,在 BigQuery 上可能就是一笔可观的扫描费用。迁平台时真正要重新设计的,是每个大模型的分区、聚簇与物化策略——这也是第四章性能调优要展开的内容。
选型时三块部件怎么取舍,给你一个工程上的经验判断:个人学习与小型团队,Core 加免费的调度手段完全够用,不产生任何订阅费用;中型团队一旦出现「多环境管理混乱、执行历史查不到、文档没人更新」三类症状中的两类,Cloud 的费用通常比自建运维的人力便宜;已重仓某朵云数仓的团队,也可以考察该仓库官方的数据平台是否已内置 dbt 集成,避免多维护一套调度。
⚠️ 常见坑:连接配置(仓库地址、账号、密钥)绝对不要写进项目仓库。它包含凭据,且随环境而变——本地开发、持续集成、生产调度各有一份,由环境变量或部署流程分别注入。把凭据提交进版本库是 dbt 项目里最常见也最严重的安全事故,第六章安全一节还会回到这个话题。
Core 是标准的命令行程序,用 Python 包管理器安装,装完即得一组命令。对新手友好的是:安装不依赖任何数据库——没有仓库也能编译项目,只是不能执行。学习路径可以完全离线起步:装 Core、初始化示例项目、跑编译命令看产物,等真的要跑数据时再申请仓库账号。这个「先编译后连接」的离线可能性,也解释了为什么 CI 流水线的编译检查不需要仓库凭据以外的资源。
要。依赖解析与编译行为在版本之间存在细节差异,两个同事用不同版本,可能出现「我这编译通过、你那报错」的幽灵问题。工程做法是把版本号钉在项目的依赖声明文件里,配合一个锁文件机制保证全团队安装到同一版本——思路与 4.2 节「包版本钉死」完全一致:所有影响构建行为的输入都要可复现。
不会冲突,但建议按需精简。适配器只是额外的 Python 包,装多少个互不干扰,连接配置里用「目标」区分走哪个适配器。真正的取舍是维护面:项目声明了五个适配器,等于五行「这些平台都是我们的支持范围」,每次方言差异排查都要考虑五个变量。多数项目的现实是主力仓库一家、偶尔加一家做迁移——声明你真在用的,卸掉你只是「可能会用」的。
下一节我们换个视角:把 dbt 和它要替代的老办法放在同一张桌子上比一比,看依赖、测试、文档、复用四个维度上,三种方案各得几分。