专家(二):Claude Code × 数据工程------Airflow + dbt + Spark 全流程
Windows 10/11 · Claude Code v2.1.32+ · DeepSeek V4 Pro / Anthropic API · 🟡 中度时效 · 最后更新 2026-05-18
一、这篇教程解决什么问题
一句话定位:读完本篇,你会用 Claude Code 从零搭建一条电商数据分析管道------从原始日志到 BI 看板,覆盖 dbt 数据建模(Star Schema)、Airflow DAG 编排(TaskFlow API + 动态任务 + SLA 监控)、PySpark 批处理(Join 优化 + 数据倾斜修复)、Great Expectations 数据质量自动化检查,全程 AI 主导、你来审校。
这是专家系列第二篇。上一篇我们用 Claude Code 拆了 6 个微服务,这篇换一个赛道------数据工程。数据工程是 AI 辅助开发覆盖最少、但需求增长最快的领域:大量 YAML/SQL/Python 胶水代码、配置文件驱动的工具链、重复性高的管道搭建------这些恰恰是 Claude Code 最擅长的事。
真实案例速览:
| 案例 | 规模 | AI 辅助效果 | 来源 |
|---|---|---|---|
| SimilarWeb: Spark 作业优化 | 200 台机器 / 22h 运行 | 90x 加速(22h→15min),成本降 160x | DataFlint 2026 |
| Cisco: Codex 嵌入生产流水线 | 全球工程团队 | 月省 1,500+ 工程小时,构建时间 -20% | The Applied 2026 |
| Reco.ai: JSONata 引擎重写 | JS→Rust, 13K 行代码 | 7h 完成,年省 $500K | AI for Automation 2026 |
| Wix: Airflow 告警 AI Agent | 生产级 Airflow 管道 | 月省 675 工程小时,15% 自动修复率 | ZenML 2026 |
阅读前提(硬条件):
- 读过高手进阶(二)Routines 云端自动化、高手进阶(六)CI/CD
- 了解 SQL 基础(能写 SELECT/JOIN/GROUP BY)和 Python 基础
- 本地安装了 Docker Desktop + Python 3.11+
- 了解 Airflow / dbt / Spark 的基本概念(不需要精通,知道各工具做什么即可)
DeepSeek 用户注意:全部可用。本文全程使用 DeepSeek V4-Pro 完成,每个阶段记录 Token 消耗。文末附完整成本对比。
读完能得到什么:
- 一套 dbt 数据建模的 Claude Code 实操方法------从读现有表结构到设计 Star Schema 到生成可运行的 dbt 模型
- Airflow DAG 完整编排------TaskFlow API + TaskGroup + 动态任务 + SLA 监控 + Slack 告警,全部由 Claude Code 生成
- PySpark 批处理优化------Claude Code 自动诊断数据倾斜、选择最优 Join 策略、修复 OOM
- Great Expectations 数据质量自动化------AI 自动生成 Expectation Suite,覆盖空值/唯一性/分布/引用完整性
- 真实数据------每个阶段的 Token 消耗、代码量、人工修改比例
- 5 个真实 Debug(DAG 调度冲突、dbt 依赖环、Spark OOM、数据倾斜、GE 误报)
二、项目全景:我们要建什么
2.1 业务背景
一个名为 ShopAnalytix 的电商数据分析平台。业务跑了 14 个月,积累了约 2.7TB 数据------但分析还停留在"运营跑一条 SQL 等 3 分钟,复制到 Excel 做透视表"的阶段。
现有数据资产:
postgresql://analytics_db/
├── raw_logs/ # 原始埋点日志(~1.8TB, 日均 4.5GB 增量)
│ ├── access_logs # 页面访问日志
│ ├── click_events # 点击事件流
│ └── order_events # 订单事件流(JSONB)
├── ecommerce/ # 业务数据库同步(~900GB)
│ ├── orders # 订单主表(~50K 行/天)
│ ├── order_items # 订单明细
│ ├── products # 商品信息
│ ├── users # 用户信息(~1.2M 注册用户)
│ └── categories # 类目层级
└── external/ # 外部数据
├── ad_campaigns # 广告投放数据
└── inventory_snapshots # 库存快照
典型痛点:
- 运营想看"过去 7 天各渠道 GMV 趋势"→ 数据分析师手写 SQL 跑 8 分钟 → 导出 Excel 做图 → 第二天数据更新了又要重跑
- 市场想看"大促期间用户行为漏斗"→ 涉及 5 张表 JOIN,SQL 写了 200 行 → 两周后才交出来
- 老板想看"库存周转率实时大屏"→ 数据源分散在 3 个系统,没有统一的数据仓库
- 每次做周报:手工跑 15 条 SQL,复制粘贴到 5 个 Excel Sheet,手动更新图表
2.2 目标架构
┌──────────────────────────────────┐
│ Metabase BI Dashboard │
└───────────────┬──────────────────┘
│
┌───────────────┴──────────────────┐
│ dbt Star Schema │
│ dim_user / dim_product / │
│ dim_date / fact_orders / │
│ fact_pageviews / mart_* │
└───────────────┬──────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PySpark Batch │ │ dbt Transform │ │ Data Quality │
│ - 日志清洗 │ │ - SQL+Jinja │ │ - GE Suites │
│ - Sessionization│ │ - 增量/全量 │ │ - 空值/唯一性 │
│ - 预聚合 │ │ - 物化策略 │ │ - 分布漂移 │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└─────────────────────────┼─────────────────────────┘
│
┌────────────┴────────────┐
│ Apache Airflow 3.2 │
│ DAG 编排 · 调度 · 告警 │
└────────────┬────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Raw Logs (S3) │ │ PostgreSQL │ │ External APIs │
│ JSON/Parquet │ │ 业务库同步 │ │ 广告/库存 │
└──────────────────┘ └──────────────────┘ └──────────────────┘
2.3 管道全景
| 层级 | 工具 | 数据形态 | 调度 |
|---|---|---|---|
| Ingest(采集) | Airflow DAG | 原始 JSON/CSV → Parquet | 每小时增量 |
| Process(处理) | PySpark 4.1 | 清洗 → Sessionization → 预聚合 | 每小时 |
| Transform(转换) | dbt-core 1.11 | Staging → Intermediate → Marts | 每小时 |
| Quality(质量) | Great Expectations 1.17 | 空值/唯一性/分布/引用完整性 | 每次 dbt run 后 |
| Serve(服务) | Metabase | BI 看板/Ad-hoc 查询 | 实时 |
2.4 工具版本(2026 年 5 月最新)
| 工具 | 版本 | 发布日期 |
|---|---|---|
| Apache Airflow | 3.2.1 | 2026-04-22 |
| dbt-core | 1.11.9 | 2026-05-06 |
| PySpark | 4.1.1 | 2026-01-09 |
| Great Expectations | 1.17.0 | 2026-05-05 |
三、阶段一:数据建模------让 Claude Code 读表结构、设计 Star Schema、生成 dbt 模型
3.1 核心策略
数据建模最大的坑不是"不会建",而是"建完后发现漏了业务方真正关心的维度"。Claude Code 在这个阶段的核心价值:一次性读完所有表结构 → 从业务需求反向推导 Star Schema → 自动生成可运行的 dbt 模型文件。传统方式需要 2-3 天的手工分析,Claude Code 一小时内完成。
在项目根目录启动 Claude Code 交互模式:
bash
cd shopanalytix && claude
3.2 第一步:全局 Schema 分析
第一条 prompt(Plan 模式,只读):
分析 PostgreSQL 数据库中所有表的 Schema 和关系。不要修改任何文件。
任务:
1. 连接 analytics_db,列出所有 schema 下的所有表及字段
2. 分析每个字段的类型、是否可空、是否有索引
3. 通过字段名推断表间关联关系(如 orders.user_id → users.id)
4. 识别可能的数据质量问题(如缺少主键的表、未规范化的 JSONB 字段)
5. 将分析结果写入 docs/SCHEMA_ANALYSIS.md
使用 Read 和 Bash 工具。数据库连接信息在 .env 文件中。
Claude Code 读取了 18 个表的 Schema 后,产出了关键发现:
- 5 个表缺少主键 :
raw_logs.order_events、raw_logs.click_events、external.ad_campaigns - 3 个 JSONB 字段承载结构化数据 :
order_events.payload(含 23 个嵌套字段)、ad_campaigns.targeting、click_events.properties - 隐式外键 14 处 :字段名暗示关联但无数据库级约束(如
order_items.order_id→ 无 FK 到orders.id) - 2 个表存在字段命名不一致 :
users用created_at,orders用create_time
3.3 第二步:业务需求 → Star Schema
基于 Schema 分析报告,第二条 prompt:
基于 docs/SCHEMA_ANALYSIS.md,设计一个电商分析的 Star Schema 数据仓库。
业务方需要回答的核心问题:
1. 各渠道/类目/时间段的 GMV、订单量、客单价趋势
2. 用户行为漏斗:浏览→加购→下单→支付,各环节转化率
3. 商品库存周转率和滞销预警
4. 广告投放 ROI(按 campaign/channel)
5. 用户分群:RFM 模型(最近购买时间/频率/金额)
输出要求:
1. 维度表设计(dim_*):表名、字段、SCD 策略(Type 1/2)
2. 事实表设计(fact_*):粒度、维度外键、度量字段
3. 每个 dbt 模型的 materialization 策略(table/view/incremental)
4. 数据血缘图(哪个事实表依赖哪些维度表)
5. 输出到 docs/STAR_SCHEMA_DESIGN.md
Claude Code 给出的 Star Schema 设计:
维度表:
| 维度表 | 来源表 | SCD 策略 | 说明 |
|---|---|---|---|
dim_user |
ecommerce.users | Type 2 | 用户等级/会员状态可能变化 |
dim_product |
ecommerce.products + categories | Type 1 | 商品信息更新即覆盖 |
dim_date |
生成 | 静态 | 预生成 2020-2030 日期维度 |
dim_channel |
external.ad_campaigns | Type 1 | 渠道映射表 |
dim_category |
ecommerce.categories | Type 1 | 类目层级(支持多级) |
事实表:
| 事实表 | 粒度 | 数据源 | 物化策略 | 预估日增量 |
|---|---|---|---|---|
fact_orders |
一行=一个订单 | orders + order_items | incremental(按 order_date) | ~50K 行/天 |
fact_pageviews |
一行=一次页面访问 | raw_logs.access_logs | incremental(按 event_time) | ~2M 行/天 |
fact_daily_sales |
一行=一天/一商品 | fact_orders 聚合 | table(全量重建) | ~5K 行/天 |
fact_inventory |
一行=一天/一商品/一仓库 | inventory_snapshots | snapshot | ~10K 行/天 |
3.4 第三步:生成 dbt 模型
确认 Star Schema 设计后,第三条 prompt:
基于 docs/STAR_SCHEMA_DESIGN.md,生成完整的 dbt 项目。
要求:
1. 按 dbt 最佳实践组织目录结构:models/staging/、models/intermediate/、models/marts/
2. staging 层:从源表做最小清洗(重命名、类型转换、去重)
3. intermediate 层:跨表 JOIN 和业务逻辑
4. marts 层:dim_* 维度表 + fact_* 事实表 + analytics/ 分析聚合
5. 每个模型包含 schema.yml(字段描述 + data tests: unique / not_null / accepted_values / relationships)
6. 使用 Jinja 宏处理重复逻辑(如货币单位统一、渠道归因、RFM 分群)
7. dbt_project.yml 配置 materialization 策略
生成所有文件到 dbt/ 目录。
Claude Code 生成的文件结构:
dbt/
├── dbt_project.yml
├── packages.yml # dbt_utils + dbt_expectations
├── profiles.yml
├── macros/
│ ├── currency_conversion.sql # 统一货币换算
│ ├── channel_attribution.sql # 渠道归因(last-touch)
│ └── rfm_segmentation.sql # RFM 用户分群
├── models/
│ ├── staging/
│ │ ├── schema.yml
│ │ ├── stg_orders.sql
│ │ ├── stg_order_items.sql
│ │ ├── stg_products.sql
│ │ ├── stg_users.sql
│ │ ├── stg_access_logs.sql
│ │ └── stg_ad_campaigns.sql
│ ├── intermediate/
│ │ ├── schema.yml
│ │ ├── int_order_details.sql # 订单宽表 JOIN
│ │ ├── int_user_sessions.sql # 用户会话化视图
│ │ └── int_daily_ad_performance.sql
│ └── marts/
│ ├── dim/
│ │ ├── dim_user.sql
│ │ ├── dim_product.sql
│ │ ├── dim_date.sql
│ │ └── dim_channel.sql
│ ├── fact/
│ │ ├── fact_orders.sql
│ │ ├── fact_pageviews.sql
│ │ └── fact_daily_sales.sql
│ └── analytics/
│ ├── mart_gmv_trend.sql # GMV 趋势
│ ├── mart_funnel.sql # 用户行为漏斗
│ ├── mart_rfm_segments.sql # RFM 分群
│ └── mart_ad_roi.sql # 广告 ROI
└── tests/
└── custom/
└── assert_positive_gmv.sql
关键:Claude Code 生成的是可以直接 dbt run 的代码 ,不是伪代码。每个 .sql 文件都包含完整的 SELECT/CTE/JOIN/WHERE 逻辑。
参考 :dbt Labs 2026 年发布的 dbt-agent-skills(GitHub:
dbt-labs/dbt-agent-skills)在 ADE-bench 基准测试中,Claude + Agent Skills 的模型生成准确率达到 53.5%(基线 Claude 为 46.5%)。而 Paradime DinoAI v3.0(基于 Claude 4.6 + 上下文图谱)达到了 88.37%。差距来自"是否连接了实际数据仓库"------让你的 Claude Code 能直接读表结构是提升准确率的关键。
3.5 阶段一数据
| 指标 | 数值 |
|---|---|
| Token 消耗 | ~35K |
| DeepSeek V4-Pro 费用 | $0.06 |
| 耗时 | 1h 20min |
| 生成文件数 | 26 个(22 个 .sql + 4 个 .yml) |
| 人工修改 | 2 处(dim_date 生成逻辑 + ad_campaigns 字段映射) |
| 人工修改比例 | ~8% |
四、阶段二:ETL 管道------Airflow DAG + dbt 转换 + Great Expectations
4.1 核心策略
ETL 管道的"胶水代码"是数据工程中最耗时、最无聊的部分------写 YAML 配置、拼 DAG 依赖、处理重试逻辑。Claude Code 在这个阶段的价值:一次性生成完整 DAG(含 TaskFlow API + TaskGroup + SLA + Notifier),并自动插入数据质量检查节点。
业界基准数据:Markaicode(2026)用 Sonnet 4 批量生成 50 个 Airflow DAG 的首次成功率是 76%,平均生成时间 8 秒,失败主要来自导入错误(18%)和动态 DAG 语法(6%)。
4.2 第一步:生成 Airflow DAG
基于 dbt/ 目录的模型,创建 Airflow DAG 编排完整数据管道。
要求:
1. 一个主 DAG:shopanalytix_daily.py,每天凌晨 2:00 UTC 运行
2. DAG 结构使用 TaskGroup:
- TaskGroup 1: ingest(从 S3/DB 拉取增量数据)
- TaskGroup 2: spark_process(PySpark 清洗 + Sessionization)
- TaskGroup 3: dbt_run(staging → intermediate → marts)
- TaskGroup 4: data_quality(Great Expectations checkpoint + dbt test)
- TaskGroup 5: notify(成功/失败 → Slack)
3. 使用 TaskFlow API(@task 装饰器,不是传统 Operator)
4. 关键配置:
- schedule="0 2 * * *"
- catchup=False
- is_paused_upon_creation=True(防止部署时误触发回填)
- max_active_runs=1
- retries=2, retry_delay=timedelta(minutes=5)
- SLA: dbt_run 组 30 分钟内完成,total 60 分钟
5. 使用 Airflow 3.x 的 Notifier 抽象做告警(非 on_failure_callback 裸写)
6. 环境变量和连接从 Airflow Variables/Connections 读取
生成文件到 airflow/dags/shopanalytix_daily.py。
Claude Code 生成的 DAG 核心结构:
python
# airflow/dags/shopanalytix_daily.py
from airflow.decorators import dag, task, task_group
from airflow.utils.dates import days_ago
from airflow.providers.slack.notifications.slack import (
send_slack_notification,
SlackNotifier,
)
from datetime import timedelta, datetime
@dag(
dag_id="shopanalytix_daily",
schedule="0 2 * * *",
start_date=datetime(2026, 1, 1),
catchup=False,
is_paused_upon_creation=True, # 防止部署时误触回填
max_active_runs=1,
default_args={
"retries": 2,
"retry_delay": timedelta(minutes=5),
},
sla_miss_callback=send_slack_notification(
text=":warning: SLA miss in shopanalytix_daily!"
),
on_failure_callback=SlackNotifier(
channel="#data-alerts",
text=":x: shopanalytix_daily FAILED",
),
tags=["shopanalytix", "daily"],
)
def shopanalytix_daily():
@task_group(group_id="ingest")
def ingest():
@task
def pull_raw_logs():
"""从 S3 拉取增量日志到本地 Parquet"""
...
@task
def sync_ecommerce_db():
"""增量同步业务库到 staging 区"""
...
@task
def fetch_external_apis():
"""拉取广告/库存 API 数据"""
...
return [pull_raw_logs(), sync_ecommerce_db(), fetch_external_apis()]
@task_group(group_id="spark_process")
def spark_process(inputs):
@task
def spark_clean_logs():
"""PySpark 清洗 + sessionize"""
...
@task
def spark_pre_aggregate():
"""预聚合大表"""
...
return [spark_clean_logs(), spark_pre_aggregate()]
@task_group(group_id="dbt_run")
def dbt_run(inputs):
@task.bash
def dbt_staging():
return "cd /opt/dbt && dbt run --select staging --profiles-dir ."
@task.bash
def dbt_intermediate():
return "cd /opt/dbt && dbt run --select intermediate --profiles-dir ."
@task.bash
def dbt_marts():
return "cd /opt/dbt && dbt run --select marts --profiles-dir ."
dbt_staging() >> dbt_intermediate() >> dbt_marts()
@task_group(group_id="data_quality")
def data_quality(inputs):
@task.bash
def great_expectations_check():
return "cd /opt/ge && great_expectations checkpoint run shopanalytix_checkpoint"
@task.bash
def dbt_test():
return "cd /opt/dbt && dbt test --profiles-dir ."
return [great_expectations_check(), dbt_test()]
@task_group(group_id="notify")
def notify(inputs):
@task
def send_success():
"""Slack 通知成功 + 数据行数统计"""
...
# 编排
ingested = ingest()
processed = spark_process(ingested)
transformed = dbt_run(processed)
quality_checked = data_quality(transformed)
quality_checked >> notify(quality_checked)
dag = shopanalytix_daily()
4.3 第二步:Great Expectations 数据质量
为 dbt 模型的 4 张核心表(fact_orders、fact_pageviews、dim_user、mart_gmv_trend)
生成 Great Expectations Expectation Suite。
对每张表自动检测并配置:
1. 空值检查:所有不可为空的字段 → expect_column_values_to_not_be_null
2. 唯一性检查:主键字段 → expect_column_values_to_be_unique
3. 值范围检查:GMV/金额字段 ≥ 0
4. 引用完整性:外键字段引用维度表主键
5. 分布检查:对数值字段生成分位数检查
6. 行数检查:日增量不低于历史 4 周同日均值的 60%(避免固定阈值误报)
生成文件到 great_expectations/expectations/。
Claude Code 自动生成的关键点:它不只是生成 JSON 配置文件,而是在读取了 dbt schema.yml 中的字段定义后 ,根据实际字段语义生成检查规则。例如读到 total_amount DECIMAL(12,2) 会自动加 >= 0 约束,读到 user_id INTEGER NOT NULL 会自动加 not_null + 引用完整性检查。
注意:Great Expectations Cloud 有 ExpectAI(AI 驱动的 Expectation 推荐引擎),但它仅限 GX Cloud(开源 Core 不可用),且存在过度拟合当前数据快照的风险------今天的脏数据可能被奉为明天的验收标准。本教程采用 Claude Code 生成规则 + 人工审核的方式,不走 ExpectAI 自动审批路径。
4.4 阶段二数据
| 指标 | 数值 |
|---|---|
| Token 消耗 | ~42K |
| DeepSeek V4-Pro 费用 | $0.07 |
| 耗时 | 1h 35min |
| 生成文件数 | 8 个(1 个 DAG + 5 个 GE Suite + 2 个 Slack 通知模板) |
| 人工修改 | 3 处(SLA 阈值调整 + GE 分布阈值 + Slack channel 细分) |
五、阶段三:PySpark 批处理------让 Claude Code 生成并优化 Join 策略
5.1 核心策略
PySpark 的难点不在 API 本身,而在性能调优 ------同样的逻辑,Join 策略和分区方式不同,性能差 10-100 倍。Claude Code 在这个阶段的核心价值不是"写 PySpark 代码",而是分析数据特征 → 自动选择最优 Join 策略 → 写出带 shuffle/broadcast/partition/salting 优化的代码。
业界参考:SimilarWeb 用 AI Copilot(DataFlint)诊断一个在 200 台机器上跑了 22 小时失败的 Spark 作业,发现是非确定性 UDF 触发了重复执行------4 行修复代码将作业从 22h 降到 15min(90x 加速 ),成本从 200 台机器降到 20 台(160x 降本)。
5.2 第一步:Sessionization(用户会话化)
原始日志是逐条的,分析用户行为需要先做 Sessionization------把同一用户 30 分钟内的连续点击合并到一个 Session。
写一个 PySpark job 做用户会话化。
数据源:raw_logs.access_logs(Parquet, 日均 ~2M 行, ~4.5GB)
字段:user_id, event_time, page_url, referrer, user_agent, ip_address
逻辑:
1. 按 user_id 分组,按 event_time 排序
2. 如果相邻两次事件间隔 > 30 分钟,切断为新 Session
3. 每个 Session 输出:session_id, user_id, start_time, end_time,
page_count, entry_page, exit_page, channel(从 referrer 解析)
性能要求:
- 日增量处理时间 < 5 分钟
- 使用 Window 函数 + lag() 做 Session 切割
- 结果按 ds(日期)分区存储为 Parquet
先分析数据特征(user_id 基数、时间跨度、数据倾斜风险),再写代码。
Claude Code 的分析过程:
[分析] 读取 access_logs 统计信息...
- user_id 基数: ~1.2M(高基数,不适合 broadcast)
- 日均行数: ~2M
- 数据倾斜: 前 1% 用户(机器人/爬虫)贡献 23% 事件
- 建议: 加盐(salting)处理倾斜 user_id
[生成] spark/jobs/sessionize_events.py
生成的代码核心逻辑:
python
# spark/jobs/sessionize_events.py
from pyspark.sql import SparkSession, Window
from pyspark.sql.functions import (
col, lag, when, sum as spark_sum, row_number,
rand, concat, lit
)
spark = SparkSession.builder \
.appName("ShopAnalytix-Sessionization") \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
.config("spark.sql.shuffle.partitions", "400") \
.getOrCreate()
def sessionize(ds: str):
"""用户行为会话化。ds: 日期分区,格式 YYYY-MM-DD"""
raw = spark.read.parquet(f"s3://shopanalytix/raw/access_logs/ds={ds}")
# 1. 加盐处理数据倾斜 ------ 前 1% 用户占 23% 数据
salted = raw.withColumn(
"salt", (rand() * 10).cast("int")
).withColumn(
"salted_user_id", concat(col("user_id"), lit("_"), col("salt"))
)
# 2. Window 函数 ------ 计算相邻事件时间间隔
user_window = Window.partitionBy("salted_user_id").orderBy("event_time")
with_lag = salted.withColumn(
"prev_event_time", lag("event_time", 1).over(user_window)
)
# 3. 超过 30 分钟 = 新 Session
with_session_flag = with_lag.withColumn(
"is_new_session",
when(
col("prev_event_time").isNull()
| (col("event_time").cast("long")
- col("prev_event_time").cast("long") > 1800),
1
).otherwise(0)
)
# 4. 累积求和生成 session_id
session_window = Window.partitionBy("salted_user_id").orderBy("event_time")
with_session_id = with_session_flag.withColumn(
"session_id_raw", spark_sum("is_new_session").over(session_window)
).withColumn(
"session_id",
concat(col("user_id"), lit("_"), lit(ds), lit("_"), col("session_id_raw"))
)
# 5. 聚合每个 Session 的首末事件
session_window_agg = Window.partitionBy("session_id")
result = with_session_id \
.withColumn("rn", row_number().over(session_window_agg.orderBy("event_time"))) \
.filter(col("rn") == 1) \
.select(
"session_id", "user_id",
col("event_time").alias("start_time"),
# ... 更多字段
)
result.write.mode("overwrite").partitionBy("ds").parquet(
f"s3://shopanalytix/warehouse/sessions/ds={ds}"
)
5.3 第二步:Spark Join 优化
Session 数据出来后,需要 JOIN 订单数据做转化率分析。但初次运行性能很差。
分析 spark/jobs/analyze_conversion.py 中的 JOIN 性能瓶颈。
两张表:
- sessions(~2M 行/天,~8GB)
- orders(~50K 行/天,~200MB)
当前代码用 spark.sql() 写的 JOIN,跑了 12 分钟。
请:
1. 分析数据倾斜情况和 JOIN 策略
2. 识别是否适合 broadcast join
3. 建议最优策略并修改代码
4. 合理解释为什么新策略更快
Claude Code 的分析和修复:
[分析] 当前执行计划:
SortMergeJoin [user_id] ← 两边都有 shuffle,shuffle write ~3.2GB
[发现]
- orders 表仅 50K 行/天,200MB,完全可放入 driver 内存
- 但 spark.sql.autoBroadcastJoinThreshold 默认 10MB,不会自动 broadcast
- sessions 表 user_id 有倾斜
[建议] 调高 autoBroadcastJoinThreshold 到 256MB,让 Spark 自动选择 BHJ
python
# 修复前 (SortMergeJoin, 12 min)
df = spark.sql("""
SELECT s.*, o.order_id, o.total_amount
FROM sessions s
JOIN orders o ON s.user_id = o.user_id
WHERE s.ds = '2026-05-15'
""")
# 修复后 (BroadcastHashJoin, < 3 min)
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", "268435456") # 256MB
from pyspark.sql.functions import broadcast
df = sessions.alias("s").join(
broadcast(orders.alias("o")), # 显式 broadcast
on="user_id",
how="left"
).filter(col("s.ds") == "2026-05-15")
5.4 阶段三数据
| 指标 | 数值 |
|---|---|
| Token 消耗 | ~31K |
| 费用 | $0.05 |
| 耗时 | 55 min |
| 生成文件数 | 2 个 PySpark job |
| 性能提升 | SortMergeJoin 12min → BroadcastHashJoin <3min(4x+) |
| 人工修改 | 1 处(广播阈值按集群实际内存调整) |
六、阶段四:监控告警------Airflow SLA + dbt 测试 + Slack 告警
6.1 核心策略
数据管道建好后,最大的风险不是"跑不动",而是"跑完了但数据是错的"------而等你发现时下游的报表和决策已经错了三天。Claude Code 在这个阶段的价值:把监控和告警也自动化------一次性生成 SLA 规则、dbt data tests、分级 Slack 通知模板。
6.2 第一步:dbt data tests + 自定义测试
为所有 dbt 模型添加完整的 data tests。
要求:
1. schema.yml 中每个关键字段至少 1 个 test:
- 主键:unique + not_null
- 外键:relationships(引用完整性)
- 金额/数量:dbt_utils.expression_is_true("amount >= 0")
- 状态字段:accepted_values
2. 自定义测试:
- assert_positive_gmv.sql:每日 GMV > 0
- assert_no_future_dates.sql:所有日期字段 < current_date
- assert_conversion_rate.sql:转化率在 0-100% 之间
3. 严重性分级:
- warn:行数偏差 < 20%
- error:行数偏差 >= 20%、唯一性、引用完整性
修改 dbt/models/**/schema.yml。
6.3 第二步:Airflow SLA + 分级告警
完善 Airflow DAG 的监控能力:
1. SLA 阈值:
- ingest 组: 15 分钟
- spark_process 组: 10 分钟
- dbt_run 组: 30 分钟
- data_quality 组: 10 分钟
- 总 DAG: 60 分钟
2. 分级告警:
- SLA miss → Slack #data-alerts + PagerDuty(可能影响下游)
- dbt test 失败 → Slack #data-quality,附失败 test 列表
- GE validation 失败 → Slack #data-quality,附 expectation 详情
- DAG 成功 → Slack #data-notifications(安静模式:仅行数统计摘要)
3. 数据新鲜度监控(独立 DAG):
- 每日凌晨 6:00 检查前一天分区是否到位
- 若源表无新数据 → 延迟 30 分钟重试 → 仍无数据 → 升级告警到 #data-alerts
修改 airflow/dags/shopanalytix_daily.py,新增 airflow/dags/shopanalytix_monitor.py。
参考:Wix 工程团队的 AirBot(2026)用 GPT-4o Mini + Claude 4.5 Opus 构建 AI 驱动的 Airflow 告警 Agent,集成 GitHub/Trino/Spark/OpenMetadata 的 MCP 服务器,实现了每月节省 675 工程小时、15% 自动修复率,平均每次交互成本仅 $0.30。
6.4 阶段四数据
| 指标 | 数值 |
|---|---|
| Token 消耗 | ~22K |
| 费用 | $0.04 |
| 耗时 | 50 min |
| 新增文件 | 3 个(schema.yml 更新 + monitor DAG + Slack 模板) |
| 人工修改 | 2 处(PagerDuty routing key + Slack channel 细分) |
七、成本全记录
7.1 全项目 Token 消耗汇总
| 阶段 | Token | DeepSeek V4-Pro 费用 | 耗时 | 生成文件 | 人工修改率 |
|---|---|---|---|---|---|
| 一:数据建模 | ~35K | $0.06 | 1h 20min | 26 | ~8% |
| 二:ETL 管道 | ~42K | $0.07 | 1h 35min | 8 | ~12% |
| 三:PySpark 批处理 | ~31K | $0.05 | 55 min | 2 | ~5% |
| 四:监控告警 | ~22K | $0.04 | 50 min | 3 | ~9% |
| 合计 | ~130K | $0.22 | 4h 40min | 39 | ~8.5% |
7.2 传统方式估算对比
| 阶段 | 传统估算 | AI 辅助实际 | 加速比 |
|---|---|---|---|
| 数据建模(Schema 分析 + Star Schema) | 2-3 天 | 1h 20min | ~12x |
| ETL 管道(DAG + dbt + GE) | 3-5 天 | 1h 35min | ~18x |
| PySpark 批处理(含优化) | 2-3 天 | 55 min | ~15x |
| 监控告警 | 1-2 天 | 50 min | ~10x |
| 合计 | 8-13 天 | 4h 40min | ~16x |
7.3 如果用 Claude Opus 4.7
| 指标 | DeepSeek V4-Pro | Claude Opus 4.7 | 倍率 |
|---|---|---|---|
| Token 消耗 | ~130K | ~130K(相同场景) | 1x |
| 总费用 | $0.22 | ~$2.60 | 11.8x |
| 输出质量 | 90% 可用的代码 | 95% 可用的代码 | 5% 质量差 |
结论:数据工程场景下(SQL/YAML/Python 胶水代码为主),DeepSeek V4-Pro 的质量完全够用,成本仅为 Opus 的 1/12。
八、复盘:Claude Code 在数据工程中的优劣
做得好的地方
| 场景 | 为什么适合 AI | 业界验证 |
|---|---|---|
| YAML/配置文件生成 | 结构固定、可模板化,AI 填入正确字段基本不会出错 | Airflow DAG 首次成功率 76%(Markaicode 2026) |
| SQL SELECT/JOIN | 给定 Schema 后,AI 写出正确 SQL 的准确率极高 | dbt + Agent Skills 准确率 53-88%(dbt Labs ADE-bench) |
| dbt 模型生成 | staging → intermediate → marts 层次清晰,AI 照搬模板 | 72% 数据团队使用 AI 辅助编码(dbt Labs 2026) |
| 数据质量规则 | GE expectation 的字段名和约束逻辑高度结构化 | ExpectAI 可自动建议规则(但需人工审核) |
| Airflow DAG 骨架 | TaskFlow API 的装饰器模式非常规范,AI 不易偏离 | 平均 8s 生成一个 DAG,45 行代码 |
需要人工盯的地方
| 场景 | 为什么需要盯 | 怎么盯 |
|---|---|---|
| 业务逻辑理解 | AI 无法自行判断"渠道归因用 last-touch 还是 linear" | 在 prompt 中明确业务规则 |
| 数据倾斜诊断 | AI 能写 salting 代码,但不能直接连 Spark UI 看执行计划 | 跑完代码后检查 Spark UI 的 Stage 详情 |
| 性能阈值设定 | SLA 时长、广播阈值、分区数------这些是经验值 | 按实际数据量调整,记录在 CLAUDE.md |
| JOIN 正确性验证 | AI 的 JOIN ON 条件可能语法正确但语义错误(LEFT vs INNER) | 检查 JOIN 前后行数变化 |
| 安全/连接串 | 数据库连接、API Key、Slack webhook------绝不能硬编码 | 走 Airflow Variables/Connections/Secrets |
数据工程 vs 应用开发:AI 效用差异
| 维度 | 应用开发(微服务) | 数据工程(本文) |
|---|---|---|
| 代码/配置比 | 90% 代码 + 10% 配置 | 40% 代码 + 60% 配置 |
| AI 擅长度 | 代码生成好,配置容易出错 | 配置生成极好,SQL 极好 |
| 最棘手问题 | 架构设计 + 分布式一致性 | 数据倾斜 + 性能调优 |
| AI 最佳角色 | 主力编码 + 审校 | 管道搭建 + 质量检查 |
| 人的核心价值 | 架构决策 + Code Review | 业务理解 + 性能阈值 + JOIN 语义验证 |
九、Debug ×5(全项目实际踩坑)
Debug #1 --- DAG 调度冲突:部署后 DAG 疯狂回填两年数据
报错 :Metabase 看板显示 fact_daily_sales 中同一天有 3 条相同聚合数据------DAG 在同一小时跑了 3 次。查看 Airflow DAG Runs 列表,发现从 2024 年 1 月 1 日开始全部补跑。
根因 :start_date=datetime(2024, 1, 1) + is_paused_upon_creation=False 的组合------虽然 catchup=False,但 is_paused_upon_creation=False 让 Airflow 在 DAG 首次注册时就激活调度器,Airflow 仍从 start_date 计算所有"应该跑但没跑"的实例并排队执行。
| 场景 | start_date | is_paused_upon_creation | catchup | 行为 |
|---|---|---|---|---|
| 正确 | datetime(2026, 1, 1) |
True | False | 首次部署暂停,需手动 unpause |
| 错误 | datetime(2024, 1, 1) |
False | False | 部署瞬间补跑 2 年数据 |
| 修复 | datetime(2026, 1, 1) |
True | False | 安全 |
修复:
python
@dag(
start_date=datetime(2026, 1, 1),
catchup=False,
is_paused_upon_creation=True, # ← 关键:部署后需手动 unpause
)
验证 :airflow dags list + 检查 DAG Run 历史------每天只有一次 run。
Debug #2 --- dbt 模型依赖环:int_order_details 和 fact_orders 互相 ref
报错:
Found a cycle: int_order_details -> dim_user -> fact_orders -> int_order_details
根因 :Claude Code 生成的 fact_orders.sql 引用了 int_order_details,而 int_order_details.sql 的 FROM 子句中写了 {``{ ref('fact_orders') }}------两个模型互相引用形成环。dbt 的 DAG 解析器强制单向无环图,编译阶段就报错。
| 模型 | 原 ref | 实际依赖方向 | 修复 |
|---|---|---|---|
int_order_details |
ref('dim_user'), ref('fact_orders') ❌ |
派生自订单、用户、商品 | 去掉 ref('fact_orders') |
fact_orders |
ref('int_order_details') ✅ |
派生自 intermediate | 保持不变 |
关键规则 :dbt 模型的数据流是严格的 staging → intermediate → marts。intermediate 层不应引用 marts 层------这是 AI 最常见的 dbt 错误。
修复 :int_order_details 只引用 staging 表和 dim 表,不引用 fact 表。加上 dbt doc 注释明确层级。
验证 :dbt compile 通过 + dbt ls --resource-type model 显示正确的 DAG 顺序。
Debug #3 --- Spark OOM:Executor 内存溢出,YARN 直接 Kill 容器
报错:
ExecutorLostFailure: executor 5 exited caused by one of the running tasks
Reason: Executor heartbeat timed out.
Container killed by YARN for exceeding memory limits. 9.6 GB of 9 GB used.
根因 :Sessionization job 中 Window.partitionBy("user_id") 导致某个 executor 拿到了超级用户的数据------该用户(爬虫)一天产生 12 万条事件,collect_list() 在窗口内把所有 page_url 塞进数组,单个 task 的内存超过 9GB executor 限制。
Spark OOM 诊断决策树:
OOM Error
├─ Driver OOM?
│ ├─ collect()/toPandas() 用了? → 限制结果或采样
│ └─ broadcast 表太大? → 增加 driver 内存或禁用 broadcast
└─ Executor OOM? ← 我们的情况
├─ Spark UI 有倾斜 Task(max 时长 >> median)? → 加盐拆分热点 key
├─ Shuffle 数据量过大? → 增加 shuffle 分区数
└─ UDF 内存泄漏? → 替换为 Pandas UDF 或内置函数
| 方案 | 原理 | 本场景适用性 |
|---|---|---|
spark.sql.shuffle.partitions 增大 |
更均匀分布 | ❌ 无法解决单 key 倾斜 |
| 加盐(salting) | 拆分大 key 到多个分区 | ✅ 最有效 |
spark.sql.adaptive.skewJoin.enabled |
Spark 3.2+ 自动检测 | ✅ 辅助手段 |
| 增大 executor 内存 | 硬扛 | ⚠️ 治标不治本 |
修复:
python
# 加盐 ------ 把倾斜最多的 user_id 拆成 10 份
skewed_users = ["bot_crawler_001", "scraper_api_user"]
salted_df = df.withColumn(
"salted_user_id",
when(col("user_id").isin(skewed_users),
concat(col("user_id"), lit("_"), (rand() * 10).cast("int")))
.otherwise(col("user_id"))
)
验证:Spark UI → Stage 详情 → 每个 task 的 shuffle read size 差异从 800x 降到 8x。
Debug #4 --- 数据倾斜:增量 MERGE 从 3 分钟涨到 45 分钟
症状 :fact_pageviews 每天增量 MERGE 越来越慢,从最初的 3 分钟到 30 天后跑 45 分钟。每次 MERGE 都扫描全表历史。
根因 :Claude Code 生成的 fact_pageviews 使用了 merge_update_columns 包含所有字段,导致每次 MERGE 需要把增量分区的每一行和全表历史按 unique_key 做匹配------30 天后表里已有 6000 万行,增量 MERGE 开销随表大小线性增长。
| 策略 | 适用场景 | 本场景效果 |
|---|---|---|
| 全表覆盖(table) | 小表(<100 万行) | ❌ 6000 万行不可行 |
| 增量 append-only | 不可变日志(如 pageviews) | ✅ 每天只 append 新行 |
| 增量 + 部分更新 | 需要修正历史行的表 | ✅ MERGE ON ds+PK |
修复 :将 fact_pageviews 改为 incremental + append-only 策略(pageview 一旦发生不需要修改历史)。对于需要更新历史行的字段(如用户最后活跃时间),拆分到独立的 dim_user_activity 维表中。
验证:增量运行从 45min 恢复到 3min。
Debug #5 --- Great Expectations 误报:节假日正常流量下降触发告警
症状 :每天凌晨 4:00 GE checkpoint 告警------fact_daily_sales 的 expect_table_row_count_to_be_between 失败:行数 32,411 低于 min_value=35,000。但那天是法定假日,电商流量下降完全正常。
根因 :Claude Code 自动生成的 GE expectation 用了固定阈值 min_value=35000,没有考虑节假日/周末/大促等周期性波动。GE Cloud 的 ExpectAI 也有相同问题------基于当前数据快照的统计特征生成规则,把"波动"当成了"异常"。
| GE 策略 | 适用场景 | 本场景问题 |
|---|---|---|
| 固定阈值 | 稳定的内部系统 | ❌ 电商有天然周期性 |
| 同比(vs 上周同日) | 电商/零售 | ✅ 周一 vs 周一更合理 |
| 7 日移动平均 ± 30% | 去噪 | ✅ 能容忍单日波动 |
| 节假日特殊规则 | 电商 | ✅ 春节/双11/国庆单独配置 |
修复:将 GE expectation 改为比较"过去 4 周同日平均":
python
# 修复前(固定阈值,易误报)
validator.expect_table_row_count_to_be_between(
min_value=35000, max_value=100000
)
# 修复后(4 周同日移动平均 ± 30%)
# 使用 GE 的 row_condition + query-based expectation
validator.expect_table_row_count_to_be_between(
min_value={"$PARAMETER": "rolling_4w_same_day_min"},
max_value={"$PARAMETER": "rolling_4w_same_day_max"}
)
验证:后续运行------正常周末和节假日波动不再告警,但真实数据缺失(连续 3 天 < 均值 60%)正确触发了升级告警。
十、速查卡
Claude Code 数据工程 Prompt 模板
| 阶段 | 模板 |
|---|---|
| Schema 分析 | "分析数据库中所有表的 Schema。列出字段类型、索引、隐式外键、数据质量问题。输出 SCHEMA_ANALYSIS.md。只读。" |
| Star Schema 设计 | "基于 Schema 分析,设计 Star Schema。维度表(dim_)+ 事实表(fact_)。标注 SCD 策略和物化策略。" |
| dbt 模型生成 | "基于 Star Schema 设计,生成完整 dbt 项目。staging → intermediate → marts。每个模型含 schema.yml + data tests。" |
| Airflow DAG | "生成 Airflow DAG。TaskFlow API + TaskGroup。含 ingest/spark/dbt/quality/notify 五组。is_paused_upon_creation=True。" |
| PySpark + 优化 | "写 PySpark job 做 {任务}。先分析数据特征(基数/倾斜/大小),再选最优 Join 策略和分区方案。" |
| 数据质量 | "为 {表名} 生成 GE Expectation Suite。用滚动窗口(非固定阈值)处理周期性数据。" |
| 监控告警 | "为管道添加 SLA + dbt test + Slack/PD 分级告警。warn 和 error 走不同 channel。" |
成本控制参数
| 场景 | max_turns | max_budget_usd | 推荐模型 |
|---|---|---|---|
| Schema 分析(只读) | 10 | 0.10 | V4-Pro |
| dbt 模型生成(大量文件) | 20 | 0.30 | V4-Pro |
| PySpark 优化分析 | 10 | 0.15 | V4-Pro |
| DAG/YAML 生成 | 10 | 0.15 | V4-Pro |
| 调试修复 | 8 | 0.10 | V4-Pro |
报错速查
| 报错 | 根因 | 解决 |
|---|---|---|
| DAG 同一天跑多次 | is_paused_upon_creation=False + 远期 start_date |
is_paused_upon_creation=True |
Found a cycle: A → B → A |
dbt 模型互相 ref,违反单向流 | 严格 stag → int → marts,底层不 ref 上层 |
Container killed exceeding memory |
单 key 数据倾斜 + Window 全量收集 | 加盐拆分倾斜 key + AQE skew join |
| 增量 MERGE 越来越慢 | merge_update_columns 包含所有列 | append-only 策略,可变数据迁移到维表 |
| GE 固定阈值误报 | 未考虑周期性(周末/节假日波动) | 4 周同日移动平均替代固定阈值 |
CLAUDE.md 数据工程模板
数据工程的 CLAUDE.md 核心要回答五个问题:
- 仓库:名称和版本(PostgreSQL 15 / Snowflake / BigQuery)
- 转换:dbt 项目结构和模型目录
- 编排:Airflow 版本和 DAG 位置
- 目录:数据血缘和治理工具(DataHub / OpenMetadata)
- BI:最终呈现工具(Metabase / Looker / Mode)
推荐结构(500-1500 字,按实际踩坑逐行增加):
markdown
## Naming(最重要的规则)
- staging: stg_<source>__<entity>.sql
- intermediate: int_<entity>_<verb>.sql
- marts: dim_/fact_ 前缀
- 主键: <entity>_id(不是 id 或 uid)
## Testing(强制规则)
- 主键: unique + not_null
- 源表: freshness(1h=warn, 4h=error)
- 事实表外键: relationships 到对应维度表
## Safety Rules(Agent 绝对不能做)
- 永远不在生产环境运行 DROP/TRUNCATE/DELETE
- 始终用 dev target 做探索性工作
- 查询 >1TB 的表必须加分区过滤
- 所有 Agent 发起的查询标记 query_tag = 'claude-code'
## Workflow(Agent 工作流)
1. dbt ls --select +model_name+ 检查影响范围
2. 在正确的层做修改(staging/intermediate/marts)
3. 更新 schema.yml(含字段描述)
4. dbt compile → dbt run → dbt test → sqlfluff lint
扩展阅读
本系列相关文章:
- 高手进阶(二):Routines 云端自动化 --- 数据管道可配置为 Routine 定时触发,替代 Airflow 调度
- 高手进阶(六):Headless 模式与 CI/CD 集成 --- dbt/Airflow 集成到 CI/CD 管道
- 专家(一):Claude Code × 微服务------从单体拆分到 Kubernetes 部署 --- 专家系列开篇,同一 AI 辅助方法论的不同技术栈应用
- 高手进阶(八):综合实战------用 Claude Code 交付一个完整全栈项目 --- 全栈项目实战,可作为数据看板前端的参考
- 高手进阶 成本优化篇 --- DeepSeek V4 成本控制,数据工程的 Token 优化策略
参考文献
- dbt Core v1.11 Documentation --- dbt 官方文档,模型/materialization/tests 参考
- Apache Airflow 3.2 Documentation --- Airflow 官方文档,DAG/TaskFlow/Notifier 机制
- PySpark 4.1 SQL Programming Guide --- Spark SQL + DataFrame API 官方指南
- Great Expectations 1.17 Documentation --- GE 官方文档,Expectation/Checkpoint 配置
- Kimball Dimensional Modeling Techniques --- Star Schema 设计方法论
- dbt Labs: dbt Agent Skills (GitHub) --- dbt 官方 AI Agent 技能包,9 个技能覆盖建模/语义层/排错
- Astronomer: AI Agent Tooling for Airflow --- Airflow 官方 AI Agent 工具
- dbt Labs: State of Analytics Engineering 2026 --- 72% 数据团队使用 AI 辅助编码
- Spark Optimization Guide: Broadcast Join --- Spark 性能调优官方指南
- SimilarWeb Case Study: AI Spark Optimization --- 200台机器22h→15min,90x 加速案例
- Wix AirBot: AI On-Call Assistant for Airflow --- AI 驱动 Airflow 告警,月省 675 工程小时
- Markaicode: AI DAG Generation Benchmark (2026) --- 50 个 DAG 首次成功率 76%
- Dataworkers: CLAUDE.md for Data Projects --- 数据工程 CLAUDE.md 最佳实践