数据工程终极设计模式——构建端到端数据管道

23 阅读26分钟

引言

数据不会停留在一个地方。它会同时在 mobile apps、server logs、sensor networks 和 transaction systems 中生成。在这些数据能够驱动 dashboards、触发 alerts,或输入 machine learning models 之前,它们需要被移动、转换,并落入一个能够让数据可访问且有用的系统中。

欢迎来到 data pipelines 的世界。

简单 cron jobs 每晚推送一次 CSV files 的时代已经过去。现代 data pipelines 是复杂的分布式系统,设计目标是每秒摄入数百万 events、实时转换数据,并在可扩展架构中高效存储数据。它们被期待具备 resilience、observability,并且能够动态扩展。

在本章中,我们将首先从 batch processing pipelines 入手。你将逐步了解如何从 files 或 databases 摄入数据,如何使用 Apache Spark 转换数据,并将 output 存储到 data warehouse 或 data lake 中。这是结构化、周期性 workflows 的骨干,也是理解基础概念的良好起点。

随后,我们将进入 real-time streaming 的快车道。你将探索 Kafka 如何支持 event-driven ingestion,Apache Flink 如何处理流动中的数据,以及 Apache Druid 等系统如何支持 low-latency analytics,从而实现即时洞察。

但构建 pipeline 不只是把工具连接起来。我们还会探索如何编排复杂 workflows、监控 performance、优雅处理 failures,并通过 CI/CD 和 infrastructure-as-code 原则自动化 deployments。

到本章结束时,你不仅会理解现代 data pipelines 如何构建,还将掌握构建 scalable、production-grade systems 的实践方法,使 raw data 真正释放生命力。

结构

本章将覆盖以下主题:

  • Step-by-Step Implementation of a Batch and Streaming Pipeline
  • Data Ingestion、Processing、Storage and Serving Workflows
  • Workflow Orchestration and Automation
  • Error Handling、Monitoring and Fault Tolerance

Step-by-Step Implementation of a Batch and Streaming Pipeline

在 data-driven organization 中,pipelines 是看不见的循环系统,负责从 sources 收集、转换并交付数据到 destinations。设计良好的 pipeline 会抽象复杂性、执行可靠性,并确保数据始终在正确的时间,以正确的格式出现在正确的位置。

从核心上看,data pipeline 包含以下阶段:

Ingestion:从 databases、APIs 或 files 等 source systems 拉取数据。

Transformation:使用逻辑清洗、增强、join 或 aggregate 数据。

Storage:将数据持久化到 warehouses、lakes 或 analytical engines 中。

Serving:使数据可供 dashboards、models 或 operational apps 使用。

根据 latency 和 freshness 需求,pipelines 可分为两类:batch 和 streaming。Batch 适合 historical 或 periodic processing,而 streaming 擅长处理 real-time updates 和 time-sensitive events。

Batch Pipeline

Batch pipeline 是 data engineering 中的传统主力。它以离散 chunks,也就是 batches,处理数据,通常基于 schedule 执行。可以把它想象成收集某个 time window 内的所有数据,例如一天的数据,对其进行转换,然后将结果加载到 data warehouse 等 target system 中。

当数据不需要立即被采取行动时,这种模型非常理想。Nightly sales reports、monthly customer churn calculations 或 weekly ETL jobs,都自然适合 batch 范式。Latency 通常以分钟到小时衡量,而不是毫秒。对许多 analytics use cases 来说,这不仅可以接受,甚至更合适。

Batch pipelines 的优势在于 simplicity、throughput 和 determinism。它们允许对大规模数据进行强大的 transformations,并具备 repeatability 和 auditability。由于 input data 是 bounded 的,你确切知道哪些 records 进入了处理流程,因此它们通常更容易 debug。

不过,这也意味着 batch pipelines 不适合 real-time insights。如果 customer 完成一笔 transaction,batch system 可能要到下一次 scheduled run 才能反映出来。在要求即时响应的系统中,这种延迟可能代价高昂,例如 fraud detection、dynamic pricing 或 real-time personalization。

Use Case:零售连锁的 Daily Sales Aggregation

Objective:从每家门店的 POS system 收集 daily sales transactions,清洗并转换它们,然后将 aggregated metrics 加载到 data warehouse 中用于 reporting。

Step 1:Ingestion(Source → Raw Storage)

我们收集 store POS systems 生成的 CSV files,并将它们上传到 S3 bucket。

# Sample boto3 code to upload sales data
import boto3

s3 = boto3.client('s3')
s3.upload_file('local/sales_day1.csv', 'retail-bucket', 'raw/sales_day1.csv')

Step 2:Transformation with Apache Spark

使用 PySpark 读取 raw files,清理 nulls,计算 daily aggregates,并写入 Parquet。

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("SalesBatchPipeline").getOrCreate()

df = spark.read.csv("s3://retail-bucket/raw/", header=True, inferSchema=True)

# Basic transformation
df_clean = df.dropna(subset=["store_id", "amount"])
agg_df = df_clean.groupBy("store_id", "sale_date").sum("amount")

# Write to S3 as Parquet
agg_df.write.mode("overwrite").parquet("s3://retail-bucket/processed/daily_sales/")

Step 3:Load to Data Warehouse,例如 Redshift 或 BigQuery

使用 Apache Airflow 或 dbt 等工具,将 transformed data 从 S3 加载到 warehouse。

Airflow DAG snippet:

from airflow import DAG
from airflow.providers.amazon.aws.transfers.s3_to_redshift import S3ToRedshiftOperator

S3ToRedshiftOperator(
    task_id='load_sales_data',
    schema='public',
    table='daily_sales',
    s3_bucket='retail-bucket',
    s3_key='processed/daily_sales/',
    copy_options=['FORMAT AS PARQUET'],
    redshift_conn_id='redshift_default',
    dag=dag,
)

Before the Batch Pipeline

  • Files 必须经过 schema consistency validation。
  • File naming convention,例如 sales_YYYYMMDD.csv,有助于自动化 ingestion。
  • 确保 source systems 在一致的 cutoff time 前推送数据,例如每天午夜。

After the Batch Pipeline

  • 数据进入 warehouse 后,dashboards 会使用 Apache Superset 或 Tableau 等 BI tools 刷新。
  • Data lineage 和 audit logs 可通过 Great Expectations 或 DataHub 等工具维护。
  • Email alerts 或 Slack notifications 可以通知 stakeholders pipeline 成功或失败。

Streaming Pipeline

Streaming pipeline 是为“当下”而构建的。不同于按 scheduled intervals 分块处理数据的 batch pipelines,streaming pipelines 会在数据生成后持续处理。Events 像水流一样流经系统,从而支持 real-time insights 和 decision-making。

当 latency 不只是一个指标,而是一项要求时,streaming 就变得必不可少。Fraud detection、order tracking、real-time recommendation engines 和 IoT monitoring,都是等待一分钟都太久的场景。在这些场景中,立即对数据作出反应,可以提升 customer experience、operational efficiency 或 risk management。

Streaming pipeline 的核心是 event-driven architecture。Events,例如 user click、financial transaction 或 sensor reading,会在发生时被摄入,并即时处理。数据流没有固定的“开始”或“结束”;pipeline 始终运行。

Streaming pipelines 由为 low latency 和 high throughput 构建的工具驱动。Apache Kafka 充当 event ingestion 和 buffering 的 durable backbone。Apache Flink 或 Spark Structured Streaming 用于 in-memory transformations 和 aggregations。处理后的数据通常通过 Apache Druid 或 ClickHouse 等 low-latency stores 提供服务,使其可以被 real-time querying。

设计 streaming systems 需要权衡。你获得了 immediacy,但必须处理 event time 与 processing time、out-of-order data、exactly-once semantics 和 backpressure 等复杂问题。架构必须 fault-tolerant,并能够 horizontal scale,以应对不可预测的 workloads。

Use Case:Food Delivery Platform 的 Real-Time Order Monitoring

Objective:当 food delivery platform 上产生 orders 时,系统需要实时捕获每个 event,用 customer 和 location metadata 进行 enrichment,并 stream 到 dashboard 中,使 operations teams 可以监控 spikes、delays 或 system outages。

Step 1:Ingestion(Orders → Kafka)

每次下单时,frontend 会将 event 发送到 API,API 再将其发布到名为 orders 的 Kafka topic。

from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers='localhost:9092',
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

order_event = {
    "order_id": "ORD12345",
    "user_id": "USR5678",
    "restaurant_id": "REST09",
    "timestamp": "2025-06-02T14:23:50Z",
    "status": "placed",
    "amount": 650.0
}

producer.send('orders', order_event)

Kafka 会确保 durability,并且即使 downstream processors 变慢或暂时不可用,也能 buffer 数百万这类 events。

Step 2:Processing with Apache Flink

使用 Flink 从 orders topic 消费数据,用 static user 和 restaurant data enrich event,并计算 metrics,例如每分钟每个 city 的 total orders。

// Pseudocode (Flink Java or PyFlink)
DataStream<Order> orders = env.addSource(new FlinkKafkaConsumer<>("orders", new OrderDeserializationSchema(), properties));

DataStream<EnrichedOrder> enrichedOrders = orders
    .keyBy(order -> order.getRestaurantId())
    .map(new EnrichWithRestaurantDetails());

DataStream<OrderSummary> perCityCounts = enrichedOrders
    .keyBy(order -> order.getCity())
    .timeWindow(Time.minutes(1))
    .process(new OrderCountAggregator());

perCityCounts.addSink(new DruidSink());

这种 real-time transformation 允许团队在 operations 发生时进行监控。

Step 3:Serving via Apache Druid

Flink 将 summarized metrics 写入 Apache Druid,由 Druid 驱动 real-time dashboard。Druid 的 sub-second query latency 非常适合跨 regions、order types 或 restaurant partners 进行 slicing and dicing。

Example Druid ingestion spec:

{
  "type": "kafka",
  "dataSchema": {
    "dataSource": "order_metrics",
    "timestampSpec": {"column": "window_end", "format": "iso"},
    "dimensionsSpec": {"dimensions": ["city", "status"]},
    "metricsSpec": [{"type": "count", "name": "order_count"}]
  },
  "tuningConfig": {"type": "kafka"},
  "ioConfig": {
    "topic": "order_summary",
    "consumerProperties": {"bootstrap.servers": "localhost:9092"}
  }
}

Before the Streaming Pipeline

  • 使用 Avro 或 Protobuf 等工具定义 schemas 并执行 contracts,确保 event consistency。
  • 使用 Kafka Connect 从 operational databases stream data,用于 enrichment,例如 user metadata。
  • 如果 consistency 关键,设置 Flink checkpoints 并启用 exactly-once processing mode。

After the Streaming Pipeline

  • Operations dashboards 使用 Apache Superset 或 Grafana 实时更新。
  • Anomaly detection services 或 alerting tools 消费 stream,用于在 order surges 或 drops 出现时通知团队。
  • Historical metrics 可以通过 batch jobs backfill,并与 real-time streams 合并,以确保 completeness。

Data Ingestion、Processing、Storage and Serving Workflows

现代 data pipeline 的强度取决于其各部分的总和。每个阶段,即 ingestion、processing、storage 和 serving,都有明确目的;但它们结合在一起,形成一条无缝流,将 raw、scattered data 转换为 actionable insight。

理解这些阶段如何连接,以及每个阶段存在哪些 trade-offs 和 design decisions,对构建 robust、scalable 和 production-grade data systems 至关重要。

我们可以把 data pipeline 想象成接力赛:

Ingestion 是第一棒,从 source 获取数据并让数据开始流动。

Processing 是力量型选手,将数据转换并增强为 structured、usable format。

Storage 提供交接点,也就是 processed data 被持久化用于后续使用的位置,可以是 long-term storage,也可以是 low-latency access。

Serving 是最后一棒,在正确时间把正确数据交付给正确 user 或 system。

这些 workflows 并不限于单一技术或模式。每晚运行的 batch job 可能走一条路径,而 streaming real-time dashboard 则走另一条路径。但底层 flow 保持一致:ingest、process、store 和 serve。

接下来的 sections 中,我们将详细探索这四个阶段,考察 batch 和 streaming scenarios,并展示当今生产系统中使用的 tools、best practices 和 patterns。

Data Ingestion

每条 pipeline 都始于一个问题:数据来自哪里?

Data ingestion 是任何 data pipeline 的第一步。它是从各种 source systems 收集 raw data 并将其传输到 processing layer 或 storage destination 的过程,这些 source systems 包括 transactional databases、APIs、sensor networks、logs 和 file systems。可以把它看作你的数据系统的“嘴巴”,持续摄入新信息,使 pipeline 的其他部分能够消化并采取行动。

Ingestion Types:Batch versus Streaming

Ingestion 可以是 batch-oriented,也可以是 streaming,取决于数据如何以及何时被拉取或推送。

Batch Ingestion 基于固定 schedules 工作。它按固定 intervals 拉取 chunks of data,例如每小时或每天一次。适合 reports、snapshots,或 latency 不关键的场景。

Streaming Ingestion 实时工作。Events 会在发生时持续被推入 pipeline。它适合需要即时响应的系统,例如 fraud detection、supply chain monitoring 或 personalized recommendations。

Ingestion Modes:Pull versus Push

Ingestion 也会因谁发起 data transfer 而不同。

Pull-based ingestion 从 source 拉取数据,例如使用 scheduled jobs 查询 API 或从 database export。

Push-based ingestion 由 source 主动向你的系统发送数据,例如 IoT device 发布消息到 message queue。

Common Ingestion Sources

常见 ingestion sources 包括:

Databases:MySQL、PostgreSQL、MongoDB 等。

APIs:第三方服务的 REST endpoints。

Files:来自 FTPs 或 cloud storage 的 CSV、JSON、XML files。

Logs and Events:Application logs、user events 或 telemetry。

Streaming Sources:Kafka topics、Kinesis streams、MQTT brokers。

每种工具都提供各种 sources 的 connectors,支持 schema inference,并可扩展以便在 ingestion 期间进行 transformation 或 filtering。

总结来说,data ingestion 不只是“把数据弄进来”,它是 quality、reliability 和 scalability 的前线。如果 ingestion 做错了,pipeline 的其余部分都会继承混乱。

Data Processing

Data ingestion 负责把数据带进来,而 processing 则让数据变得有价值。

你已经看过 batch systems 如何使用 Spark 做 aggregations,以及 streaming systems 如何使用 Flink 做 real-time transformations 的详细示例。本节将聚焦于适用于二者的一些基础原则。

Schema Enforcement and Evolution

在 producers 和 consumers 解耦的数据生态中,schema 就是一份 contract。它定义 incoming 和 outgoing data 的 structure、types 和 expectations。

Schema Registry:在使用 Kafka 的 streaming architectures 中,Confluent Schema Registry 可与 Avro、Protobuf 或 JSON Schema 集成,为每个 topic 存储并执行 schemas。Producers 和 consumers 可以在 runtime 验证 messages,以避免 breaking changes。

Evolution Strategy:为了安全,使用 backward-compatible changes。这包括添加 optional fields,但避免 renamesdeletes,除非已经 versioned。

Batch Tools Support:Apache Spark 可与 Hive Metastore 或 Delta Lake 集成,以执行和跟踪 schema changes。如果 Parquet file 的 schema 发生细微变化,mergeSchema=true 可以避免 runtime errors,但如果没有审计,也可能隐藏 incompatibilities。

Note:将 schema diffs 保存在 version control 中,以追踪某个 column 何时被 added、changed 或 deprecated。

Handling Bad Data Gracefully

Bad data 不可避免,关键在于你的应对计划。

In Batch(Spark) :可以使用 DataFrame.filter()dropna(),但也应考虑使用 DataFrame.withColumn() + try-except UDFs,在不让 jobs 崩溃的情况下捕获 parsing failures。

from pyspark.sql.functions import udf
from pyspark.sql.types import DoubleType

@udf(DoubleType())
def safe_cast_to_double(val):
    try:
        return float(val)
    except:
        return None

df = df.withColumn("amount_clean", safe_cast_to_double(df["amount"]))

In Streaming(Flink) :使用 side outputs,将 corrupted 或 suspicious data 路由到 audit stream。

OutputTag<String> deadLetterTag = new OutputTag<String>("bad-events"){};

随后可以从这个 side stream 中 monitor、alert 或 replay bad events。

Dead Letter Queues(DLQs) :将 failed records 存储到 Kafka、Cloud Storage 或 S3 中用于 inspection。设置 retry policies,并在 DLQ volume 超过 thresholds 时发出 alerts。

Business Logic versus Technical Logic

拆分 transformations 可以使 processing logic modular 且 traceable。

In Batch(dbt) :dbt 的 model structure 允许你拆分 technical cleaning models,例如 stg_;business logic models,例如 int_fct_;以及 reporting views,例如 dim_marts

In Streaming(Kafka Streams / Flink) :创建专用 processing module,使用 domain-focused services,例如 CustomerScoringServiceDeliveryTimeClassifier,而不是将 raw transformations 直接嵌入 Kafka / Flink operators 中。

Tip:考虑使用 function registries 或 UDF registries,使 domain logic 可在 pipelines 之间复用,例如 fraud rules、pricing rules。

Testing and Debugging Transformations

测试 pipelines 需要同时模拟 data 和 environment。

Batch Pipelines:

  • 使用 PySpark local mode 做 unit tests。
  • 使用 Great Expectations 或 Deequ 添加 data quality validations,例如“该字段中 nulls 不超过 5%”。
  • 利用 Airflow test DAGs,并使用 moto 等 libraries mock S3 / GCS paths。

Streaming Pipelines:

  • 使用 embedded Kafka,例如通过 TestContainers 或 Redpanda,模拟 ingestion。
  • 使用 Kafka Connect with Debezium replay test events,完成基于 CDC 的 rehydration。
  • 使用 Flink 的 TestHarness 或 MiniCluster 在本地验证 streaming jobs。

Data Serving

当数据已经被摄入、转换并存储后,还有最后一个关键步骤:serving。这里是数据与其受众相遇的地方,无论受众是刷新 dashboard 的 business user、请求 features 的 machine learning model,还是触发 automated response 的 system。

Serving workflows 充当 data systems 和 consumers 之间的 interface layer。它们确保正确的数据以安全、高效、正确格式提供给依赖它的 tools、users 或 systems。

Serving 会根据 use case 采取不同形式。对 BI users 来说,它可能意味着将数据加载到 semantic layer 或 dashboard tool。对 developers 来说,它可能意味着暴露 APIs 或 feature stores。对 automated systems 来说,它可能涉及通过 stream triggers 或 materialized views 提供服务。

Serving in Batch Pipelines

在 batch workflows 中,serving 通常是 pull-based,用于 decision support、historical analysis 或 periodic reporting。目标是让 curated、queryable data 易于被 business users 和 analysts 探索。

Common Components:

  • BI Tools:Apache Superset、Tableau、Power BI
  • SQL Engines:Trino、Presto、Athena、BigQuery、Redshift Spectrum
  • Semantic Layers:Cube.js、dbt Metrics Layer、LookML(Looker)

Engineering Practices:

Materialized Views:为昂贵 queries 做 pre-aggregate,例如每个 product 的 daily sales。

Role-Based Access Control(RBAC) :使用 Apache Ranger 或 AWS Lake Formation 等工具,对 datasets 执行 fine-grained access。

Caching:使用 Redis 或 Looker result cache 等 layer,为 frequently queried reports 提供缓存。

Query Federation:使用 Trino / Starburst,以单一 SQL dialect 统一访问 Hive、S3、RDBMS 和 NoSQL systems。

示例:Daily sales pipeline 写入 BigQuery。Superset 直接连接 BigQuery,dashboards 每天早晨使用预定义 views 刷新。

Serving in Streaming Pipelines

在 streaming 中,data serving 必须是 event-driven 且 low-latency。系统不会等待有人请求,而是近实时地推送 insights、触发 alerts 或驱动 dashboards。

Common Components:

Streaming Dashboards:Apache Superset(with Druid / ClickHouse)、Redash、Metabase with real-time connectors。

Push Notifications:Kafka、webhooks 或 serverless functions,用于 alerting。

Feature Stores:Feast、Tecton,用于向 production systems 提供 ML-ready features。

Streaming APIs:将 Kafka / Flume consumers 暴露为 WebSocket 或 REST APIs,供 external services 使用。

Engineering Practices:

Backpressure Management:监控 serving endpoints,避免 overload,例如太多 dashboard users 同时访问 Druid。

Preloading Hot Data:针对 top-N queries 或 fast filters,使用 materialized views 或 in-memory segments。

Real-Time Aggregation Stores:优先选择 Pinot 或 Druid 等 databases,它们内置 rollups,并提供 fast ingestion → query pipelines。

Streaming Joins for Contextual Serving:在将数据提供给 personalization modules 之前,先在 Flink 中用 customer info enrich clickstream。

示例:Kafka-Flink-Druid pipeline 持续更新 hourly order metrics。Apache Superset dashboards 在数秒内反映数据,当 volume 偏离预期范围时,alerts 会触发到 Slack。

Data serving 是价值变得可见的地方。如果 storage 是金库,serving 就是每个人透过它看到并采取行动的玻璃窗。它决定了你只是拥有数据,还是正在使用数据。

Workflow Orchestration and Automation

设计 pipeline 只是旅程的一部分;确保它能可靠地、在正确时间、以规模化方式运行,正是 orchestration 和 automation 发挥作用的地方。本节将探索现代数据系统如何协调复杂 workflows、监控 dependencies,并以最少人工干预自动部署 changes。

我们将首先深入 workflow orchestration,也就是可靠 pipeline execution 的骨干;随后介绍将 DevOps principles 引入 data engineering 世界的 automation techniques,使 reproducibility、version control 和 hands-free operations 成为可能。

Orchestration 和 automation 共同使 data pipelines 不只是 functional,而是 production-ready。

Workflow Orchestration

构建 data pipelines 只是故事的一半;让它们可靠地、在正确时间、按正确顺序运行,是另一半。这正是 workflow orchestration 发挥作用的地方。

Orchestration 关注定义、调度和管理 data pipelines 中 tasks 的执行。在简单 setup 中,这可能是一个 cron job 触发 Python script。在现代 production-grade systems 中,它是一张由相互依赖 steps 组成的 directed graph,其中有些 steps 是 scheduled,有些是 triggered,有些是 conditional,并且在 distributed environments 中执行,具备完整 observability 和 error handling。

无论是协调每天早上运行的 ETL job,还是响应新数据到达的 real-time DAG,orchestration 都能确保 pipelines organized、reliable 且 hands-free。

The Role of Orchestration

想象一条 pipeline:

  1. 从 MySQL database 提取 customer data;
  2. 在 Spark 中清洗和增强;
  3. 加载到 BigQuery;
  4. 然后刷新 dashboard。

每个步骤都有 dependencies、timing constraints 和潜在 failure points。Orchestration 允许你:

  • 定义 task dependencies 和 ordering。
  • 设置 schedules 或 triggers。
  • 监控 status 和 logs。
  • 实施 retries 和 alerting。
  • 确保 idempotency 和 reproducibility。

它将 raw scripts 转换为 resilient、auditable workflows。

ToolBest ForHighlights
Apache AirflowBatch ETL / ELT workflowsPython DAGs、plugins、UI、schedule-based
PrefectModern orchestration with PythonDeclarative、dynamic DAGs、flow parameters
DagsterData-aware orchestrationType checks、asset lineage、testing support
LuigiLegacy pipelinesSimple,适合 smaller DAGs
KubeFlow PipelinesML workflows on KubernetesNative K8s、model lifecycle
Argo WorkflowsCloud-native pipelinesYAML-defined、Kubernetes-native

表 13.1:Popular Workflow Orchestration Tools

Engineering Practices:

  • 将 pipeline 模块化为 reusable tasks。
  • 使用 XComs 或 context variables 在 tasks 之间传递 metadata。
  • 将 workflow logs 和 metadata 存储在 centralized systems 中,例如 S3、DB、GCS。
  • 添加 SLAs 和 failure hooks,例如 Slack / email / webhooks,用于 proactive monitoring。

Orchestration in Batch versus Streaming

在 batch pipelines 中,orchestration 通常由 schedule 驱动。Jobs 会在定义好的 intervals 执行,例如每天凌晨 2 点,并且每个 task 会在下一个 task 开始前运行完成。

# Airflow example
from airflow import DAG
from airflow.operators.python import PythonOperator

dag = DAG('daily_sales_pipeline', schedule_interval='0 2 * * *')

extract = PythonOperator(task_id='extract', python_callable=extract_data, dag=dag)
transform = PythonOperator(task_id='transform', python_callable=transform_data, dag=dag)
load = PythonOperator(task_id='load', python_callable=load_to_warehouse, dag=dag)

extract >> transform >> load

在 streaming pipelines 中,orchestration 往往从 DAG-based scheduling 转向 event-driven deployment 和 monitoring。你可能部署一次 Flink job,然后持续监控它,而不是反复运行它。

即便如此,meta-orchestration 仍然非常关键,例如在 100 万 events 后触发 model refresh,或每晚轮转 partitions。这可以由 Airflow、Dagster 或 Prefect 处理。

示例:Batch job 在 S3 中出现新 partition 时触发,例如通过 S3 event 或 sensor,而不是等待 clock schedule。

Orchestration 是 one-off scripts 与 production systems 的分界线。它让 data workflows 在出现问题时也能保持 repeatable、observable 和 dependable。

Automation

Orchestration 告诉 workflows 何时以及如何运行。Automation 确保它们可以在没有人工干预的情况下被部署、更新和维护。它是连接 codebase 和 production 的粘合剂,为 data infrastructure 带来 reliability、repeatability 和 velocity。

在现代数据系统中,automation 主要有两种形式:

CI/CD for Data Pipelines:自动化 code 和 configuration 在不同 environments 之间的 testing、versioning 和 deployment。

Infrastructure as Code(IaC) :将 compute、storage 和 orchestration environments 视为 declarative、reproducible、version-controlled artifacts。

二者结合,使 teams 可以快速行动,同时不破坏系统。

CI/CD for Data Pipelines

CI/CD(Continuous Integration / Continuous Deployment)允许 teams:

  • 当 changes 被 push 时自动测试 pipeline code。
  • 将 DAGs、transformation logic 和 configs 部署到 staging 或 prod。
  • 监控 deployment health,并在需要时 rollback。

GitHub Actions、GitLab CI、Jenkins 和 CircleCI 等 CI/CD tools,可以与 Airflow、dbt 和 Spark 等 data frameworks 无缝配合。

Engineering Practices:

  • 为 UDFs 和 transformation functions 编写 unit tests。
  • 编写 integration tests,使用 test datasets 模拟 DAG execution。
  • 将 DAGs 和 pipeline logic 存储在 Git 中,并采用清晰 branching strategies,例如 dev → staging → prod
  • 在 deployment 前自动执行 validation checks,例如 schema diffs、linting 和 dependency resolution。

示例:

# GitHub Actions: Deploy Airflow DAGs
name: Deploy DAGs

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout DAGs
        uses: actions/checkout@v2

      - name: Copy DAGs to Airflow
        run: scp dags/* airflow@prod-server:/airflow/dags/

对 dbt 来说,dbt Cloud 或 dbt-core + CI/CD 等工具可以在每次 commit 时触发 model builds、tests 和 documentation。

Infrastructure as Code(IaC)

Terraform、Pulumi 和 AWS CloudFormation 等 IaC tools,允许你以 version-controlled code 定义 infrastructure,例如 clusters、storage buckets、VPCs 和 orchestration servers。这意味着:

  • Environments 可以被精确 recreated 或 cloned。
  • Staging 和 production 之间的 drift 被最小化。
  • Resources 可以被系统化 audit、scale 和 modify。

Engineering Practices:

  • 使用 reusable modules 定义 environments,例如 dev、staging、prod。
  • 将 secrets 和 configs 存储在 secure parameter stores 中,例如 AWS SSM、Vault。
  • 在 CI/CD workflows 中使用自动化 plan / apply steps。
  • 记录 resource ownership、cost estimates 和 tagging policies。

示例:

# Terraform: Create a GCS bucket for processed data
resource "google_storage_bucket" "processed_data" {
  name     = "processed-data-bucket"
  location = "US"

  versioning {
    enabled = true
  }

  lifecycle_rule {
    action {
      type = "Delete"
    }

    condition {
      age = 180
    }
  }
}

Putting It All Together

在 production data platform 中:

  1. Developer merge code,触发 CI tests 和 dbt model run。
  2. GitHub Actions 将更新后的 DAGs 部署到 Airflow。
  3. Terraform 确保正确的 buckets、clusters 和 roles 存在。
  4. Monitoring tools 监控 failures,并在问题出现时自动 alert。

这类 automation 不只是为了方便,而是 engineering discipline。它确保 pipelines 可以安全变更、快速演进,并且对 failure 具备韧性。

Error Handling、Monitoring and Fault Tolerance

即使设计最好的 data pipelines 也会失败:services crash、files missing、data malformed,或者 network issues 造成 delays。稳健 pipelines 与脆弱 pipelines 的区别,不在于它们是否失败,而在于它们如何优雅恢复、多快发出 alert,以及多透明地传达问题。

本节聚焦帮助检测、响应并从 failures 中恢复的工程策略和工具。我们将覆盖结构化 error handling 方法、proactive monitoring practices,以及如何设计能承受 production realities 的 fault-tolerant pipelines。

Error Handling

在 data pipelines 中,errors 不可避免。Input 可能 malformed,database 可能 timeout,external API 可能 down,或者 transformation 可能抛出 exception。稳健 pipeline 的目标不是消除所有 errors,而是以可预测方式处理它们,隔离其影响,并在无需人工干预的情况下恢复。

Types of Errors in Data Pipelines

Transient Errors:Network hiccups、temporary service unavailability。

Data Quality Errors:Null values、invalid formats、type mismatches。

Logical Errors:Transformation logic 中的 bugs 或 incorrect aggregations。

System Failures:Memory overflow、disk full、container crashes。

Retry Mechanisms with Backoff

Retries 是第一道防线,但盲目 retries 可能让情况变得更糟。应实施 exponential backoff,并限制 retry attempts,以避免 overload。

Airflow:

PythonOperator(
    task_id="extract_data",
    python_callable=extract,
    retries=3,
    retry_delay=timedelta(minutes=5)
)

Kafka Connect:

"errors.retry.timeout": "60000",
"errors.retry.delay.max.ms": "5000"

Flink:

env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3, Time.seconds(10)));

Dead Letter Queues(DLQs)

DLQs 是 problematic records 的 holding areas。当 pipeline 遇到 bad data 时,不应直接崩溃,而应将 record 发送到 Kafka topic 或 storage bucket,供 inspection。

Streaming Example(Flink):

OutputTag<String> deadLetterTag = new OutputTag<String>("malformed-data"){};

Batch Example(Spark):

bad_records = df.filter("amount IS NULL OR amount < 0")
bad_records.write.csv("s3://errors/bad_orders/")

始终监控 DLQ volume。Spikes 往往暗示 upstream schema changes 或 ingestion issues。

Try-Catch Logic and Null-Safe Transformations

永远不要假设数据是干净的。应在 UDFs 中使用 try-except,或在 Spark 中使用 null-safe methods。

Spark UDF:

@udf
def safe_divide(a, b):
    try:
        return a / b if b else None
    except:
        return None

Streaming systems 应使用 fallback paths。

例如,如果 enrichment 失败,不要丢弃 record,而是给它添加 "enrichment_status": "FAILED" 标签。

Data Validation Frameworks

在问题影响下游前捕获它们。

Great Expectations(Batch):

expectation_suite = context.get_expectation_suite("sales_data_suite")
batch = context.get_batch(batch_request)
results = context.run_validation_operator("action_list_operator", assets_to_validate=[batch])

Amazon Deequ(Spark + Scala):

val verificationResult = VerificationSuite()
.onData(df)
.addCheck(Check(CheckLevel.Error, "check 1")
.hasSize(_ > 1000)
.isComplete("order_id"))
.run()

Validation results 可以触发 alerts、让 DAG fail,或将 metadata 存储到 data quality dashboard 中。

Partial Failure Tolerance

并非所有 errors 都需要让整个 job fail。设计允许 partial success 的 pipelines:

  • Skip problematic records
  • Isolate failed partitions
  • 写入成功处理的数据,同时标记 issues

示例:如果一个 batch job 正在处理 100 个 files,其中 2 个 corrupt,那么应标记这些 files,继续处理其余 files,并 alert team 做 inspection,不要阻塞整个 pipeline。

Monitoring

如果 error handling 是系统的免疫反应,那么 monitoring 就是它的神经系统。它告诉你 pipelines 内部正在发生什么:什么在运行、什么失败了、什么变慢了,并且在 users 或 stakeholders 注意到之前提供信号。

Monitoring 将你的数据系统从黑盒转变为透明、可观察且可信的服务。它让 teams 有信心 scale,有上下文 debug,并有洞察进行 optimization。

Key Items to Monitor in a Data Pipeline

Monitoring 覆盖多个维度:

System Metrics

  • CPU、memory、disk I/O
  • Container restarts 或 pod health(Kubernetes)

Application Metrics

  • Number of records processed
  • Processing time per stage
  • Streaming systems 中的 lag,例如 Kafka consumer lag
  • DAGs 中的 success / failure counts

Data Quality Signals

  • Null rates
  • Schema mismatches
  • Data arrival delays,例如 expected file 未出现在 S3 中

User Impact

  • Dashboard latency
  • Stale reports
  • API call failures

Monitoring Tools and Practices

拥有正确的 observability strategy 只是成功的一半;另一半是选择合适工具,并有效集成到 data stack 中。Pipeline 不同部分会生成不同类型 signals:orchestration frameworks 记录 task statuses,streaming systems 发出 throughput 和 lag metrics,而 data validation frameworks 则暴露 payload 本身的 anomalies。

下面我们来看 batch 和 streaming workflows 中常见 monitoring tools,以及确保 visibility、debuggability 和 actionability 的工程实践。

Batch Monitoring(Airflow、dbt、Spark)

Airflow:内置 UI 显示 task status、logs、retries 和 duration。可以使用 Prometheus + Grafana 等 external tools,通过 Airflow 的 statsd exporter 提取 metrics。

dbt Cloud:包含 run history、test results 和 alerts。Self-hosted setups 可以将 metrics 推送到 monitoring stacks。

Spark:使用 Spark UI(runtime 时)或接入 Spark Listener 发出 metrics。

Best Practice:在每个 step 中记录关键 stats,例如 number of records in、out、errors encountered、partition keys used。

Streaming Monitoring(Flink、Kafka、Druid)

Flink

  • 暴露 records per second、watermark progress、checkpoint times 等 metrics。
  • 使用 Flink Dashboard,或将 custom metrics 发到 Prometheus。

Kafka

  • 监控 consumer group lag,也就是发布的数据与读取的数据之间的延迟。
  • Burrow、Cruise Control 或 Confluent Control Center 等工具可提供 broker 和 topic health 的可见性。

Druid / ClickHouse

  • 跟踪 ingestion rate、segment sizes、query latencies 和 real-time ingestion failures。

Best Practice:定义 SLOs,例如 99% 的 messages 应在 1 分钟内被处理,并跟踪 violations。

Alerting and Dashboards

没有 alerting 的 monitoring,就像看着暴风雨却没有警报器。

使用 Grafana、Datadog、CloudWatch 或 PagerDuty 来:

  • 设置 thresholds,例如 DAG run failed 超过 3 次。
  • 通过 Slack、Email 或 PagerDuty 触发 alerts。
  • 随时间可视化 metrics,以检测 trends。

Alerting Anti-Patterns to Avoid:

  • Too many alerts → alert fatigue。
  • Alerts 没有上下文 → 难以行动。
  • 没有为 expected behavior 做 suppression,例如 planned downtime。

Structured Logging and Tracing

有时 metrics 还不够,你需要追踪某个具体 job 或 record 的生命周期。

Structured Logs:使用 JSON logs,并包含 pipeline_idjob_run_idpartition_keyerror_code 等 fields。发送到 ELK、Fluentd 或 Cloud Logging。

Distributed Tracing:OpenTelemetry、Jaeger 或 Honeycomb 等工具允许你跨 services 追踪 requests,例如 API → Kafka → Flink → Druid

示例:当某条 record 在 Flink 中 validation failed 时,记录它的 order_id、failure reason 和 originating topic,并附上 job_run_id。这样更容易搜索。

Monitoring 是 pipeline 的 early warning system。它帮助你防止小问题演变为 outages,确保透明度,并让团队对自己构建的系统有信心。

Fault Tolerance

Fault tolerance 是系统在存在 partial failures 时仍能继续运行的能力。在 data pipelines 中,这意味着能够从 crashes 中恢复、安全 replay data、防止 corruption,并避免因为一个 bad component 拖垮整个系统。

Checkpointing and Savepoints in Stream Processing

在 streaming systems 中,fault tolerance 高度依赖 state management。Aggregators 和 joins 等 operators 会维护 internal state;如果 job 失败,必须恢复这些 state,以避免输出不一致。

Checkpointing:周期性 snapshot running job 的 state。如果失败,则从最近一次成功 checkpoint 恢复。

Savepoints:手动触发的 checkpoints,适合 upgrades 或 controlled redeployments。

Apache Flink:

env.enableCheckpointing(60000);  // every 60 seconds
env.setStateBackend(new RocksDBStateBackend("s3://flink-checkpoints/"));

Best Practice:

  • 将 checkpoints / savepoints 存储在 durable、distributed file system 中,例如 S3、HDFS。
  • 使 checkpoint frequency 与 data criticality 对齐,interval 越短,恢复越快。
  • 监控 checkpoint success rate 和 duration。

Idempotent Writes and Exactly-Once Semantics

在 failure 后 reprocessing data 时,存在同一数据被多次写入的风险。这会导致 duplicates、inconsistencies 和 downstream chaos。

为了避免这种情况:

  • 让 sinks idempotent,例如使用 ON CONFLICT DO NOTHING 插入。
  • 使用 deduplication keys,例如 event_idrun_id
  • 如果支持,确保 transactional writes,例如 Kafka → PostgreSQL 使用 Debezium + Kafka Connect。

示例:在 Spark Structured Streaming 中,使用唯一 _batch_id 和 merge logic 写入 Delta Lake,避免覆盖正确数据。

Exactly-Once versus At-Least-Once versus Best-Effort

Fault-tolerant systems 经常需要做取舍:

Delivery GuaranteeWhat It MeansWhen to Use
At-Least-Once可能处理 duplicates更快、更简单,但需要 dedup
Exactly-Once无 duplicates、无 losses关键应用,例如 billing、transactions
Best Effort无 guarantees非关键 logs、metrics 等

表 13.2:Fault Tolerant System 的 Trade-offs 对比

Flink 和 Kafka Streams 支持 exactly-once,但它需要:

  • Kafka transactional writes。
  • 正确的 sink support,例如 idempotent DB writes。
  • 对齐 checkpointing 和 commit protocols。

High Availability for Orchestration

Airflow 或 Dagster 等 batch orchestrators 本身也必须具备 resilience:

  • 使用 Celery 或 KubernetesExecutor 进行 distributed execution。
  • 运行多个 scheduler replicas,并支持 failover。
  • 使用 persistent metadata DB,例如 PostgreSQL,并配置 backup。

Tip:

  • 设置 task-level retries 和 DAG-level SLA monitoring。
  • 定期 snapshot metadata DBs 和 logs。

Fallbacks、Timeouts and Circuit Breakers

有时,快速且安全地失败,比无限期挂起更聪明。

Timeouts:为每个 task 设置 max run durations,避免 zombie jobs。

Fallbacks:如果 API 失败,跳过 enrichment,或使用 cached response。

Circuit Breakers:通过停止 retry storms 防止压垮失败系统,例如在 microservices 中使用 Resilience4j 或 Polly。

真实示例:如果 logistics enrichment pipeline 中使用的 weather API 失败,job 会跳过该 batch 的 enrichment,但记录 warning 并继续执行,确保核心数据仍然流动。

构建 fault-tolerant pipelines,就是预期 failure、为 failure 做计划,并隔离 failure 影响。这让 teams 能够有信心 deploy、安稳睡觉,并无惧 scale。

结论

本章探索了构建现代 data pipelines 真正需要的内容:从摄入 raw data 到交付 refined insights。我们超越了简单 ETL logic,拥抱真实世界数据系统的复杂性,详细覆盖了 batch 和 streaming architectures。通过动手 walkthroughs,我们展示了如何从多样 sources 摄入数据,如何使用 scalable frameworks 转换数据,如何高效存储数据,以及如何以同时支持 real-time decision-making 和 historical analysis 的方式提供数据服务。

但 pipelines 不只是移动数据。本章也考察了使其 reliable 和 maintainable 的基础设施与实践。从使用 Airflow 和 Prefect 等工具进行 workflow orchestration,到 CI/CD pipelines 将 automation 和 discipline 引入 deployments,再到 infrastructure-as-code setups 确保 environments 之间一致,我们说明了将 prototype 转化为 production-grade system 所需要的内容。在这个过程中,我们也处理了 distributed environments 中的现实问题:如何优雅处理 errors、如何在每个阶段监控 performance,以及出现问题时如何快速恢复。

关键教训是:data pipelines 不只是 code,它们是 systems thinking。它们必须 observable、testable、versioned、resilient,并且为 scale 而设计。最好的 pipelines 不只是交付数据,它们还在组织中交付 trust、reliability 和 velocity。

下一章中,我们将从构建 pipelines 转向在压力下保持它们运行。Operationalizing data pipelines 涉及确保其在 production 中具备 reliability、scalability 和 performance。我们将深入实践,使 teams 能够有信心部署、清晰监控,并以韧性处理 failures。你将学习如何为 pipelines 实施 CI/CD,如何使用 Prometheus 和 DataDog 等 monitoring 和 alerting tools,如何构建 automatic retry 和 recovery mechanisms,以及如何针对 cost 和 resource efficiency 优化 pipelines。到最后,你不仅会具备创建 pipelines 的坚实基础,也能在真实世界中持续且有效地运行它们。