news 2026/9/12 6:36:59

Data Engineering Zoomcamp 本地环境搭建实战:用 DuckDB + dbt Core 构建 Analytics Engineering 开发环境

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Data Engineering Zoomcamp 本地环境搭建实战:用 DuckDB + dbt Core 构建 Analytics Engineering 开发环境

Data Engineering Zoomcamp 本地环境搭建实战:用 DuckDB + dbt Core 构建 Analytics Engineering 开发环境

【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp

本指南以 Data Engineering Zoomcamp 2027 届 Module 4(Analytics Engineering)的本地环境配置文档为核心,讲解如何在个人电脑上以DuckDB 作为本地分析数据库、dbt Core 作为转换框架,完整跑通一个真实 dbt 项目(taxi_rides_ny)的从零安装、数据装载、连接验证到编辑器提效的整条链路。读完本文,你将掌握~/.dbt/profiles.yml的完整配置语义、2019–2020 纽约出租车数据的本地装载方案、dbt debug的验证方法,以及 VS Code 下 dbt 开发扩展的正确选型,并能在小内存机器上通过内存调优与增量构建稳定运行整个项目。

背景:为什么选择 DuckDB + dbt Core 作为本地方案

Module 4 的完整 dbt 项目位于 cohorts/2027/04-analytics-engineering/taxi_rides_ny,其中包含 staging → intermediate → marts 三层模型(stg_yellow_tripdatastg_green_tripdataint_trips_unionedint_tripsfct_trips以及若干维度表)。要本地运行这套模型,需要一个无需云账号、可在内存中高速执行 SQL 的数据库——DuckDB 正合适:它是进程内(in-process)嵌入式分析数据库,不需要单独的服务进程,也天然适配 dbt 这种"模型即 SQL"的开发方式。

本仓库为 Module 4 提供了两条配置路径:

  • 本地路径(本文主题):DuckDB + dbt Core,参见 setup/local_setup.md;
  • 云端路径:BigQuery + dbt Cloud,参见 setup/cloud_setup.md。

本地路径适合零成本、离线练习与快速迭代;若机器内存不足(4 GB 以下),官方更推荐改走云端路径或 GitHub Codespaces(详见下文"内存与性能调优"一节)。

整个配置流程共 6 步:安装 DuckDB → 安装 dbt → 配置 dbt Profile → 下载并装载数据 → 验证连接 → 安装 VS Code 扩展。所有 dbt 命令都必须在taxi_rides_ny/目录内执行,因为 dbt_project.yml 中通过profile: 'taxi_rides_ny'指明了项目对应的 dbt Profile 名称。

第 1 步:安装 DuckDB

DuckDB 是本地分析负载下的理想选择:它运行在你的进程内,无需网络、无需账号,天然适配教学与实验场景。安装方式有两种:

  1. 安装 CLI:按操作系统从 DuckDB 官方安装说明下载对应版本的可执行文件;
  2. 安装 Python 客户端pip install duckdb

官方文档的偏好是安装 CLI,但两种方式皆可。需要注意:本教程后续的数据装载脚本会以 Python 方式import duckdb调用其 API,而 dbt 侧则是通过dbt-duckdb适配器驱动 DuckDB,因此两种方式在链路中都会被用到(至少 Python 包必须安装)。

第 2 步:安装 dbt 与 DuckDB 适配器

一条命令安装 dbt 全家桶:

pip install dbt-duckdb

该命令会同时安装两个组件:

  • dbt-core:dbt 核心框架,负责解析项目、编译 SQL、管理模型依赖与执行计划;
  • dbt-duckdb:dbt 的 DuckDB 适配器,让 dbt 能够连接并操作 DuckDB 数据库。

提示:如果你打算日后切换云数仓,dbt 的适配器机制允许你通过安装不同适配器(如dbt-bigquery)无缝切换目标数据库,而模型 SQL 大多无需改动——这正是 taxi_rides_ny 项目中sources.yml用 Jinja 条件分支同时支持两种后端的原因(见下文第 3 步的源码印证)。

第 3 步:配置 dbt Profile(连接 DuckDB)

3.1 不需要dbt init

本仓库已内置完整的 dbt 项目taxi_rides_ny/,因此不要运行dbt init新建项目,只需为已存在的项目配置连接。dbt 通过~/.dbt/profiles.yml找到数据库连接信息,Profile 名称必须与项目中的profile: 'taxi_rides_ny'一致。

3.2 完整 Profile 配置

创建或更新~/.dbt/profiles.yml

taxi_rides_ny: target: dev outputs: # DuckDB Development profile dev: type: duckdb path: taxi_rides_ny.duckdb schema: dev threads: 1 extensions: - parquet settings: memory_limit: '2GB' preserve_insertion_order: false # DuckDB Production profile prod: type: duckdb path: taxi_rides_ny.duckdb schema: prod threads: 1 extensions: - parquet settings: memory_limit: '2GB' preserve_insertion_order: false # Troubleshooting: # - If you have less than 4GB RAM, try setting memory_limit to '1GB' # - If you have 16GB+ RAM, you can increase to '4GB' for faster builds # - Expected build time: 5-10 minutes on most systems

3.3 每个参数的语义与调优建议

参数含义说明
target默认激活的输出目标设为dev,日常开发默认走 dev;构建生产数据时用--target prod显式切换
type适配器类型duckdb对应dbt-duckdb适配器
pathDuckDB 数据库文件路径相对路径基于执行 dbt 命令的目录(即taxi_rides_ny/),会在该目录生成taxi_rides_ny.duckdb文件
schema模型落库的 Schemadev 与 prod 使用不同 schema(dev/prod),实现开发与生产数据隔离
threads并行构建模型的线程数设为 1 可显著降低并发构建带来的内存峰值压力,小内存机器的推荐值
extensions需要自动加载的 DuckDB 扩展本项目的 Parquet 数据装载依赖parquet扩展,必须保留
memory_limitDuckDB 内存上限默认 DuckDB 会用掉约 80% 系统内存,容易触发 OOM;建议设为总内存的约 50%(如 8 GB 机器设'4GB'),详见内存调优文档
preserve_insertion_order是否保持插入顺序设为false可让 DuckDB 减少排序开销、节省内存,换取构建速度

参数取值建议(来自duckdb_troubleshooting.md):内存低于 4 GB 时把memory_limit降到'1GB';16 GB 以上可上调至'4GB'加速构建。多数机器上整个项目首次构建约需 5–10 分钟。

3.4 源码印证:Profile 名称如何与项目绑定

打开项目的 dbt_project.yml 可以看到:

name: 'taxi_rides_ny' version: '1.0.0' require-dbt-version: [">=1.7.0", "<3.0.0"] profile: 'taxi_rides_ny' model-paths: ["models"] test-paths: ["tests"] seed-paths: ["seeds"] macro-paths: ["macros"] snapshot-paths: ["snapshots"]

几点关键信息:

  • profile: 'taxi_rides_ny'~/.dbt/profiles.yml中的顶层键一一对应,这是 dbt 解析连接的入口;
  • require-dbt-version: [">=1.7.0", "<3.0.0"]限定了 dbt 版本区间,保证可复现性,请确保你的dbt-core在此范围内;
  • *-paths定义了 dbt 在不同目录查找模型、测试、种子、宏与快照,配置必须与仓库目录结构一致。

另外,sources.yml通过 Jinja 条件分支同时兼容 DuckDB 与 BigQuery 两种后端,佐证了本配置跨数仓的可移植设计(models/staging/sources.yml):

sources: - name: raw database: | {%- if target.type == 'bigquery' -%} {{ env_var('GCP_PROJECT_ID', 'please-add-your-gcp-project-id-here') }} {%- else -%} taxi_rides_ny {%- endif -%} schema: | {%- if target.type == 'bigquery' -%} nytaxi {%- else -%} prod {%- endif -%}

即:DuckDB 路径下,原始数据源指向taxi_rides_ny数据库的prodschema;BigQuery 路径下则指向GCP_PROJECT_ID项目的nytaxi数据集。

第 4 步:下载并装载 NYC 出租车数据

4.1 装载脚本全貌

Profile 配置完成后,需要把 2019–2020 年黄色(yellow)与绿色(green)出租车数据装载进 DuckDB。官方提供的脚本如下(保存为load_data.py或在taxi_rides_ny/目录下直接执行):

import duckdb import requests from pathlib import Path BASE_URL = "https://github.com/DataTalksClub/nyc-tlc-data/releases/download" def download_and_convert_files(taxi_type): data_dir = Path("data") / taxi_type data_dir.mkdir(exist_ok=True, parents=True) for year in [2019, 2020]: for month in range(1, 13): parquet_filename = f"{taxi_type}_tripdata_{year}-{month:02d}.parquet" parquet_filepath = data_dir / parquet_filename if parquet_filepath.exists(): print(f"Skipping {parquet_filename} (already exists)") continue # Download CSV.gz file csv_gz_filename = f"{taxi_type}_tripdata_{year}-{month:02d}.csv.gz" csv_gz_filepath = data_dir / csv_gz_filename response = requests.get(f"{BASE_URL}/{taxi_type}/{csv_gz_filename}", stream=True) response.raise_for_status() with open(csv_gz_filepath, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) print(f"Converting {csv_gz_filename} to Parquet...") con = duckdb.connect() con.execute(f""" COPY (SELECT * FROM read_csv_auto('{csv_gz_filepath}')) TO '{parquet_filepath}' (FORMAT PARQUET) """) con.close() # Remove the CSV.gz file to save space csv_gz_filepath.unlink() print(f"Completed {parquet_filename}") def update_gitignore(): gitignore_path = Path(".gitignore") # Read existing content or start with empty string content = gitignore_path.read_text() if gitignore_path.exists() else "" # Add data/ if not already present if 'data/' not in content: with open(gitignore_path, 'a') as f: f.write('\n# Data directory\ndata/\n' if content else '# Data directory\ndata/\n') if __name__ == "__main__": # Update .gitignore to exclude data directory update_gitignore() for taxi_type in ["yellow", "green"]: download_and_convert_files(taxi_type) con = duckdb.connect("taxi_rides_ny.duckdb") con.execute("CREATE SCHEMA IF NOT EXISTS prod") for taxi_type in ["yellow", "green"]: con.execute(f""" CREATE OR REPLACE TABLE prod.{taxi_type}_tripdata AS SELECT * FROM read_parquet('data/{taxi_type}/*.parquet', union_by_name=true) """) con.close()

4.2 脚本做了什么

  1. 流式下载:从 DataTalksClub 的 NYC TLC 数据仓库(BASE_URL)按{类型}_tripdata_{年}-{月}.csv.gz命名规则,下载 2019–2020 共 24 个月 × 2 种车型的压缩 CSV;stream=True+ 8 KB 分块写入避免一次性载入内存;
  2. 断点续传:若同名 Parquet 已存在则跳过(Skipping ...),支持中断后重跑;
  3. 格式转换:用 DuckDB 的COPY ... TO ... (FORMAT PARQUET)把 CSV.gz 转成列式 Parquet,转换后立即删除 CSV.gz 释放磁盘;
  4. 装载入库:打开taxi_rides_ny.duckdb,创建prodschema,并用read_parquet('data/{taxi_type}/*.parquet', union_by_name=true)将每个目录下所有 Parquet 文件合并装载为prod.yellow_tripdataprod.green_tripdata两张表——union_by_name=true保证跨文件字段按名称对齐合并;
  5. 维护 .gitignore:自动把data/目录写入.gitignore,避免数 GB 的原始数据被误提交进 Git。

注意:数据装载的目标 schema 是prod,而~/.dbt/profiles.yml中 dev 与 prod 两个 target 的 schema 分别为dev/prod。也就是说,原始数据常驻prodschema,dev 目标下的模型则在其devschema 中构建——这与 sources.yml 中 DuckDB 分支schema: prod的指向完全吻合。因此,用 dev 目标跑模型时,dbt 同样能读到prodschema 中的原始表。

4.3 数据规模与下载耗时

该脚本下载的 yellow/green 出租车数据横跨 24 个月,包含数千万行记录(DuckDB 需在本地内存中完成装载与后续建模),下载耗时取决于网络状况,通常需要数分钟。如果你的网络较慢或磁盘紧张,可以先只跑部分月份验证链路,再全量下载。

第 5 步:验证 dbt 与 DuckDB 的连接

taxi_rides_ny/目录下执行:

dbt debug

dbt debug会依次校验:

  • dbt 版本与require-dbt-version约束是否满足;
  • 能否定位到~/.dbt/profiles.yml中的taxi_rides_nyProfile;
  • 能否用devtarget 建立到 DuckDB 的实际连接(若连接成功会输出 "Connection test: OK" 之类的成功信息)。

这是配置正确性的第一道关卡:任何 Profile 路径、schema 名称或扩展配置错误都会在此暴露。连接通过后,即可按后续课程视频运行dbt builddbt testdbt docs generate等命令。

第 6 步:为 VS Code 安装 dbt Power User 扩展(可选但推荐)

6.1 为什么不用官方 dbt 扩展

dbt Labs 官方发布的dbt Extension(基于新的 dbt Fusion 引擎)要求 dbt Fusion,不支持 dbt Core。而本项目使用的是 dbt Core + DuckDB 的本地开发方式,因此需要选择社区维护的dbt Power User by AltimateAI扩展,理由如下:

  • 与 dbt Core 完全兼容(而非仅支持 dbt Cloud);
  • 支持所有 dbt 适配器,包括 DuckDB;
  • 由社区积极维护且开源;
  • 为本地开发提供丰富的功能集。

6.2 dbt Power User 的核心能力

  • dbt 模型的 SQL 语法高亮与格式化;
  • 列级血缘(lineage)可视化;
  • 对 dbt 模型、source、宏的自动补全;
  • 交互式文档预览;
  • 直接在编辑器内完成模型编译与执行。

6.3 安装步骤

  1. 打开 VS Code;
  2. 进入扩展面板(快捷键Ctrl+Shift+X/Cmd+Shift+X);
  3. 搜索 "dbt Power User";
  4. 安装dbt Power User by AltimateAI(注意别装成 dbt Labs 的版本)。

至此,你的本地 dbt 环境已完全就绪。后续的模型运行、测试与文档构建会在课程视频中逐步讲解。

进阶:用仓库真实模型跑通 dbt 生命周期

连接验证通过后,建议按 dbt 项目分层顺序逐步构建,既能验证链路,又能观察每层的物化策略(这些策略定义在 dbt_project.yml 的models:段:staging 物化为 view、intermediate 与 marts 物化为 table):

dbt build --select stg_yellow_tripdata dbt build --select stg_green_tripdata dbt build --select int_trips_unioned dbt build --select int_trips dbt build --select fct_trips

构建过程中可以对照以下源码理解各层职责:

  • staging 层stg_yellow_tripdata.sql统一列名(vendoridvendor_idtpep_pickup_datetimepickup_datetime)、做类型转换并过滤vendorid is null的脏数据;同时通过{% if target.name == 'dev' %}只保留 2019-01 一个月的数据,实现开发环境的快速采样(stg_yellow_tripdata.sql)。这与 dbt_project.yml 中vars.dev_start_date/dev_end_date的采样意图一致;
  • intermediate 层int_trips_unioned.sqlUNION ALL合并绿黄两种车型,并通过service_type字段区分来源、补齐 yellow 车型缺失的trip_type/ehail_fee(int_trips_unioned.sql);int_trips.sqldbt_utils.generate_surrogate_key生成trip_id、join 支付类型维度表,并用QUALIFY row_number() ... = 1vendor_id, pickup_datetime, pickup_location_id, service_type去重(int_trips.sql);
  • marts 层fct_trips.sqlmaterialized='incremental'+unique_key='trip_id'+incremental_strategy='merge'配置增量物化,首次全量构建后仅按pickup_datetime增量处理新记录,并 LEFT JOIN 区域维度表补充上下车 borough/zone 信息(fct_trips.sql)。

依赖的 dbt 包(dbt_utilscodegen)声明在 packages.yml,首次构建前需运行dbt deps拉取。种子数据(如taxi_zone_lookuppayment_type_lookup)的定义与测试见 seeds/seeds_properties.yml。

进阶:内存不足(OOM)时的调优方案

DuckDB 是进程内数据库,数千万行数据的建模完全依赖本地 RAM。官方在 duckdb_troubleshooting.md 中给出了按内存规模的判断基准:

  • 4 GB RAM:很可能 OOM,建议改用 GitHub Codespaces(免费档含 4 核/8 GB 与 8 核/16 GB 机型)或云端路径;
  • 8 GB RAM:部分模型可能 OOM,需要调低内存设置;
  • 16+ GB RAM:默认设置即可顺畅运行。

若坚持本地运行,按优先级采取以下手段:

  1. 收紧profiles.yml内存参数:显式设置memory_limit(约总内存 50%)、threads: 1preserve_insertion_order: false
  2. 失败后增量续跑dbt build中途失败无需全量重来,修正问题后执行dbt retry,只重跑失败/跳过的模型;
  3. 逐个模型构建:用dbt build --select <模型名> --target prod一次只构建一个模型,降低峰值内存(见上文"用仓库真实模型跑通 dbt 生命周期"的命令序列);
  4. 利用增量模型fct_trips已配置为增量物化,首次全量构建成功后,后续运行只处理新数据,内存压力大幅下降;
  5. 环境侧优化:构建前关闭浏览器/IDE 等占用内存的应用;使用 SSD 提升 DuckDB 内存溢出落盘速度;尽量避免在 Docker 容器内运行(容器内存上限可能低于系统总内存)。

结语与后续学习路径

至此,你已经完成了 Data Engineering Zoomcamp Module 4 的本地环境搭建:安装 DuckDB 与 dbt、配置~/.dbt/profiles.yml双目标 Profile、下载并装载 2019–2020 出租车数据、通过dbt debug验证连接,并选装了适合 dbt Core 的 VS Code 扩展。接下来就可以进入课程核心内容:在 taxi_rides_ny 项目中运行模型、编写测试与生成文档。

如果想了解云端替代方案(BigQuery + dbt Cloud),参见云端配置指南;若在构建中遇到内存问题,完整的排查清单见DuckDB 内存故障排查;模块各章讲义(dbt 基础、项目结构、sources、models、seeds/macros、测试、文档、命令等)可从 04-analytics-engineering/README.md 进入。

提示:官方在本地配置文档中留了一个进阶挑战——尝试用 Docker Compose 或 Python 虚拟环境(如uv)复现整套安装流程。这不仅能加深对依赖与连接机制的理解,也是学习环境隔离的绝佳练习。

【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 6:36:41

Python lambda函数详解:从入门到高阶应用

1. 什么是Python lambda函数&#xff1f;第一次接触Python lambda函数时&#xff0c;我完全被这个奇怪的语法搞懵了。为什么要在Python中使用这种看起来像"残废"的函数&#xff1f;直到我在处理一个数据清洗任务时&#xff0c;才真正体会到它的威力。lambda函数是Pyt…

作者头像 李华
网站建设 2026/9/12 6:34:55

如何从源码构建 InsightFace Server 镜像并用本地镜像启动服务?

如何从源码构建 InsightFace Server 镜像并用本地镜像启动服务&#xff1f; 【免费下载链接】insightface State-of-the-art 2D and 3D Face Analysis Project 项目地址: https://gitcode.com/GitHub_Trending/in/insightface InsightFace Server 是一个自托管的人脸识别…

作者头像 李华
网站建设 2026/9/12 6:33:09

从prompts到skills:2025年AI编程必备的技能包实战指南

兄弟们&#xff0c;最近AI编程圈子里有个词出现频率高得吓人—— skills 。如果你还在用Claude Code或者Codex跑项目&#xff0c;大概率已经遇到过“这个skills怎么装”“那个skills好不好用”的讨论。我自己是从今年年初开始认真玩这东西的&#xff0c;从最开始一脸懵&#…

作者头像 李华
网站建设 2026/9/12 6:26:44

模板解析错误排查与解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华