数据工程终极设计模式——数据工程中的数据摄入模式

0 阅读29分钟

引言

数据摄入是每一个现代数据平台的基础。在 analytics、reporting 或 Machine Learning(ML)系统能够产生价值之前,数据必须先以可靠且可扩展的方式被收集、传输和存储。数据进入系统的方式,会显著影响 pipeline 的 performance、cost、governance,以及整体 architectural complexity。

今天的组织运行在这样一种环境中:数据以多种形式、不同速度到达。有些数据集是周期性生成的,因此可以通过 scheduled batches 处理;另一些数据则持续流动,需要 near real-time handling。在很多情况下,数据必须通过 APIs 从外部系统获取,或者直接从生成 high-velocity event streams 的 IoT devices 和 sensors 中捕获。此外,operational databases 通常需要一种机制,将 changes 传播到下游用于 analytics,这通常通过 Change Data Capture(CDC)处理。CDC 会在 database level 跟踪 inserts、updates 和 deletes,然后将这些 changes 发送出去做进一步处理,不过它的 latency 和 consistency 取决于所选择的 implementation approach。

因此,选择合适的 ingestion pattern 并不仅仅是一种技术偏好,而是一项战略性的架构决策。它取决于 business service-level expectations、可接受 latency、data volume and velocity、cost considerations、reliability guarantees、governance requirements,以及长期管理 schema evolution 的能力。当 insights 只需要周期性产出,且 infrastructure simplicity 被优先考虑时,batch-oriented design 可能已经足够。相反,当决策必须立即做出时,real-time ingestion 就变得必不可少。为了解决这些问题,API-driven ingestion 会引入对外部系统和 rate limits 的依赖,而 IoT 和 sensor-based pipelines 则需要 scalability 和 fault tolerance,以处理连续不断的数据流。

因此,本章将探索现代数据工程中使用的主要 ingestion patterns,并考察它们的架构影响。这里不会把它们作为孤立技术来呈现,而是重点理解何时以及为什么应该采用每一种方法。因此,到本章结束时,读者将能够在 scale、latency expectations、governance standards 和 operational resilience 的语境中评估 ingestion strategies,从而设计出既技术可靠、又与业务对齐的 pipelines。

结构

本章将覆盖以下主题:

  • Batch Ingestion
  • Stream Ingestion
  • Change Data Capture
  • API-Based Data Ingestion
  • IoT and Sensor Data Ingestion

Batch Ingestion

并不是所有数据都需要实时处理。许多组织天然会以数据块的形式生成数据——可以把它想象成一家零售连锁店每天结束时导出销售报表,或者一家银行每晚生成账户摘要。这些周期性的数据投递非常适合 batch ingestion。

Batch ingestion 简单来说,就是在一个定义好的时间段内收集数据,例如 hourly、daily 或 weekly,然后一次性将其加载到系统中。它就像每周洗一次衣服,而不是每穿完一件衣服就立刻洗。这个方法可靠、成本高,并且非常适合那些允许数据可用性存在轻微延迟的系统。

Batch ingestion 有两种常见类型:

  • File-Based Ingestion
  • Database Dump Ingestion

File-Based Ingestion

组织通常会将 batch data 以 files 的形式存储,例如 CSV、JSON 或 Excel sheets,放在 servers 或 cloud storage platforms 上。随后,这些 files 可以被 scripts 或 ingestion tools 拾取,并加载到 data warehouse 或 data lake 中。

示例:使用 Python 和 Pandas 摄入 CSV 文件

import pandas as pd
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
import logging

# ----------------------------
# Basic Config
# ----------------------------
CSV_PATH = "daily_sales_2025_03_30.csv"
TABLE_NAME = "sales_data"
CHUNK_SIZE = 50000

engine = create_engine(
    "postgresql+psycopg2://user:password@localhost:5432/mydb",
    pool_pre_ping=True
)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("batch_loader")

# ----------------------------
# Load in Chunks with Transaction.
# ----------------------------
total_rows = 0

try:
    for chunk in pd.read_csv(CSV_PATH, chunksize=CHUNK_SIZE):
        # Simple transformation:
        chunk["sale_date"] = pd.to_datetime(chunk["sale_date"], errors="coerce")
        chunk = chunk.dropna(subset=["sale_date"])

        # Each chunk runs inside a transaction.
        with engine.begin() as connection:
            chunk.to_sql(
                TABLE_NAME,
                con=connection,
                if_exists="append",
                index=False,
                method="multi"
            )

        total_rows += len(chunk)
        logger.info(f"Loaded {len(chunk)} rows (Total: {total_rows})")

    logger.info("Batch ingestion completed successfully.")

except SQLAlchemyError as e:
    logger.error(f"Database error occurred: {str(e)}")
except Exception as e:
    logger.error(f"Unexpected error: {str(e)}")

你还可以进一步将其调度为每天运行一次,方式包括 Apache Airflow、cron jobs,或 AWS Glue、Azure Data Factory 等 cloud tools。

Database Dump Ingestion

有时,production databases 中的整张表或选定 rows 会一次性导出,这称为 dump。这种方式适合迁移或同步大量数据。

示例:使用 MySQL 导出和导入

# Export data.
mysqldump -u root -p mydatabase sales_table > sales_table_dump.sql

# Later, import it into a reporting database.
mysql -u report_user -p reporting_db < sales_table_dump.sql

这些 dumps 也可以转换为 flat files,例如 CSV、Parquet,并加载到 data lake 中,或使用 Apache Spark、Talend 等工具处理。

因此,在以下情况下可以使用 batch ingestion:

  • 数据按可预测 intervals 到达,例如 hourly reports、daily logs。
  • Real-time processing 不是关键要求。
  • 需要高效处理大量数据。

不过,如果业务需要 instant updates,例如跟踪 app 上的 user clicks,或监控 IoT sensor readings,那么你很可能需要 real-time ingestion。

Scheduling and Orchestration

随着 data pipelines 超越单个 scripts,简单 scheduling 就不再足够。虽然 cron job 可以在固定时间执行 script,但它无法提供 task dependencies、failure recovery、observability 或 historical reprocessing 的可见性。

现代数据系统需要 orchestration,也就是以可靠性保证和 operational controls 来协调执行多个相互依赖的 tasks。

因此,当 pipelines 需要以下能力时,orchestration 就变得必要:

  • 有序 task execution,例如 validate before load。
  • Retry logic 和 failure handling。
  • 针对 historical dates 的 backfills。
  • SLA monitoring 和 alerting。
  • 对 task duration 和 failure patterns 的 observability。
  • Idempotent re-runs,并且不会造成 data corruption。

与 cron 不同,orchestration tools 会将 pipelines 建模为 Directed Acyclic Graphs(DAGs),其中每个 node 表示一个 task,edges 表示 dependencies。因此,这种结构确保 downstream tasks 只有在 upstream tasks 成功完成后才会执行。

Apache Airflow 就是一个广泛用于 batch data engineering 的 orchestration framework。

示例:Sales Ingestion DAG

下面是一个简化但贴近生产的 DAG,展示结构化摄入流程:

Extract → Validate → Load → Quality Check → Notify
from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.operators.email import EmailOperator
from datetime import datetime, timedelta
import logging

default_args = {
    "owner": "data_engineering",
    "retries": 2,
    "retry_delay": timedelta(minutes=5),
}

dag = DAG(
    dag_id="daily_sales_pipeline",
    start_date=datetime(2024, 1, 1),
    schedule="@daily",   # modern Airflow style
    catchup=False,
    default_args=default_args,
    tags=["batch", "sales"],
)

logger = logging.getLogger("sales_pipeline")

def extract(**context):
    logger.info("Extracting sales data…")
    # extraction logic
    return "extracted_file_path"

def validate(**context):
    logger.info("Validating extracted data…")
    # schema checks, null checks, row count checks

def load(**context):
    logger.info("Loading data into warehouse…")
    # load logic (idempotent insert/upsert)

def quality_check(**context):
    logger.info("Running data quality checks…")
    # row reconciliation, threshold validation

extract_task = PythonOperator(
    task_id="extract",
    python_callable=extract,
)

validate_task = PythonOperator(
    task_id="validate",
    python_callable=validate,
)

load_task = PythonOperator(
    task_id="load",
    python_callable=load,
)

quality_task = PythonOperator(
    task_id="quality_check",
    python_callable=quality_check,
)

notify_task = EmailOperator(
    task_id="notify_success",
    to="data-team@example.com",
    subject="Daily Sales Pipeline Completed",
    html_content="The daily sales pipeline completed successfully.",
)

# DAG dependencies
extract_task >> validate_task >> load_task >> quality_task >> notify_task

Data Validation and Quality Checks

在生产系统中,validation 不能依赖嵌入 transformation scripts 中的 ad-hoc assertions。因此,随着 pipelines 扩展,validation 必须变得 declarative、version-controlled 和 observable。

Data validation 有三个主要目的:

  • 执行 schema 和 structural correctness。
  • 检测 anomalies 和 data drift。
  • 防止 corrupt 或 incomplete data 传播到下游。

现代数据平台使用专门框架做 validation,而不是 inline assertions。

Schema Enforcement and Structural Validation

Great Expectations

Great Expectations 允许 engineers 以 expectations 的形式定义 data contracts,例如:

  • Column Existence
  • Data Types
  • Non-Null Constraints
  • Range Checks
  • Uniqueness Constraints

这些 expectations 是 version-controlled 的,并会生成 validation reports,因此具备 auditable 和 observable 特性。

Deequ

Deequ 常用于 Spark environments,它支持大规模 declarative data quality checks。它支持 constraint validation、anomaly detection,以及对大型 datasets 的 automated profiling。

dbt Tests

在 warehouse-centric architectures 中,dbt tests 直接在 SQL models 中提供轻量但强大的 validation。常见 tests 包括:

  • not_null
  • unique
  • relationships
  • accepted_values

这种方式确保 quality checks 嵌入 transformation layers,而不是存在于孤立 scripts 中。

Anomaly Detection and Data Drift

除了 structural validation,production pipelines 还会监控 statistical drift 和 distributions 中的异常变化,包括:

  • Row counts 突然下降。
  • Revenue 偏离 threshold bands。
  • 出现意外 categorical value。

Anomaly detection 可以通过以下方式实现:

  • Deequ Anomaly Detection Features
  • Custom Statistical Thresholds
  • ML-Based Drift Detection Frameworks

这会把 validation 从 rule-based checks 推进到 behavior-based monitoring。

File Formats and Storage Efficiency

File format selection 会影响 processing performance、storage cost 和 query efficiency。这里的选择并不只是 compression,而是数据在物理上如何组织和访问。

Columnar Formats 的重要性

Parquet 和 ORC 等 columnar formats 按列而不是按行存储数据。这支持:

Column Pruning:查询时只读取所需 columns。

Predicate Pushdown:Filters 在完整 data scanning 之前就在 storage layer 应用。

Row Group Metadata:每个 block 存储 statistics,例如 min、max 和 null count,从而允许跳过无关 data blocks。

Efficient Compression:Columns 通常包含同质数据类型,因此可以提升 compression ratios。

CSV 和 JSON 等 row-based formats 即使只需要部分 columns,也需要解析完整 file。如下表所示:

FormatStorage TypeSchema SupportCompressionQuery EfficiencyBest Use Case
CSVRow-basedNo native schemaCan be compressed(gzip、bz2)Low(full scan required)Small data exchange、simple interoperability
JSONRow-based(semi-structured)Flexible, but implicitCompressibleLow to MediumAPIs、nested event logs
ParquetColumnarStrong schemaBuilt-in(Snappy、ZSTD)High(column pruning + predicate pushdown)Analytics、data lakes、warehouses
AvroRow-based(binary)Strong schema(schema evolution supported)Compact binaryMediumStreaming pipelines、Kafka messages

表 4.1:File Formats

Practical Selection Guidelines

  • 使用 CSV:当 simple data exchange 中 compatibility 比 performance 更重要时。
  • 使用 JSON:用于 semi-structured API 或 event payloads。
  • 使用 Avro:用于 streaming systems,当需要 schema evolution 和 compact binary serialization 时。
  • 使用 Parquet(或 ORC):用于 analytical workloads 和 data lakes,当 scan efficiency 和 cost control 关键时。

Partitioning Strategy

Partitioning 可以提升 query speed,也有助于有效管理数据。

常见 Partition Keys:

  • date
  • region
  • department

示例:

/sales_data/year=2025/month=03/day=30/sales.parquet

Delta vs. Full Loads

在 batch data ingestion 中,最重要的设计决策之一,是执行 full load 还是 delta load。

Full load 会在每次执行时替换整个 dataset。这种方法简单可靠,但随着 data volumes 增长会变得低效。它通常适合数据集较小、upstream systems 无法可靠提供 change tracking,或进行 initial backfills 的场景。

相比之下,delta load 只处理自上次成功 ingestion 以来新增或修改的 records。这会降低 compute cost、network usage 和 processing time。不过,delta loading 会引入 change tracking、deduplication 和 consistency 方面的复杂性。

一个朴素的 delta 示例可能如下。

MERGE / UPSERT 的角色

Delta ingestion 必须同时考虑 inserts 和 updates。简单 append new rows 可能会产生 duplicates 或 stale records。

现代 warehouses 和 databases 支持 MERGEUPSERT operations,用于将 incoming data 与 existing records 对齐。

SQL 示例:

MERGE INTO transactions t
USING staging_transactions s
ON t.transaction_id = s.transaction_id
WHEN MATCHED THEN
UPDATE SET
amount = s.amount,
status = s.status,
updated_at = s.updated_at
WHEN NOT MATCHED THEN
INSERT (transaction_id, amount, status, updated_at)
VALUES (s.transaction_id, s.amount, s.status, s.updated_at);

这可以确保:

  • Existing records 被正确更新。
  • New records 被插入。
  • Duplicate data 被避免。

没有 reconciliation strategy 的 delta ingestion 是不完整的。

Idempotent Delta Design

设计良好的 delta pipeline 必须能够安全 rerun。Failures 可能发生在 execution 中途,而 orchestrators 可能自动 retry tasks。

为了实现 idempotency:

  • 使用 natural 或 surrogate keys 唯一识别 records。
  • 使用 MERGE,而不是 blind INSERT
  • 维护 watermark tables,跟踪上次成功 ingestion time。
  • 确保 ingestion timestamps 反映 processing time,而不只是 source time。

如果没有 idempotency,retries 可能悄悄污染 datasets。

Slowly Changing Dimensions(SCD)

在 analytical systems 中,updates 可能需要保留历史,而不是覆盖 records。这在 customer、product 或 account entities 等 dimension tables 中尤其常见。

两种常见模式是:

SCD Type 1

用最新值覆盖 existing records。不做 historical tracking。

SCD Type 2

通过插入新 records 来保留 historical changes,并包含:

  • Effective Start Date
  • Effective End Date
  • Active Flag

何时选择 Full vs. Delta

Full loads 适合以下情况:

  • Data volumes 可管理。
  • Simplicity 优先于 optimization。
  • Upstream systems 缺少可靠 change tracking。
  • 正在执行 initial historical backfills。

Delta loads 适合以下情况:

  • Data volumes 很大。
  • Latency requirements 需要 incremental updates。
  • Compute 和 storage cost 必须控制。
  • Source systems 提供可靠 change indicators。

Stream Ingestion

Stream ingestion 会在 events 生成时连续处理,而不是等待 scheduled batch intervals。它常用于 transaction monitoring、telemetry pipelines、clickstream tracking 和 operational observability systems。

不同于 batch processing,streaming systems 会引入必须显式管理的 distributed systems complexity。

大多数 streaming platforms 会提供 delivery guarantees,例如 at-most-once、at-least-once 或 exactly-once。Exactly-once processing 在实践中很难实现,因此通常依赖 idempotent downstream writes,而不是跨系统的严格协调。

Event ordering 通常只在 partitions 内得到保证,而不是 global ordering。因此,选择合适的 partition key 对平衡 ordering requirements 和 parallel scalability 至关重要。

同时,必须持续监控 consumer lag。如果 consumers 无法跟上 incoming events,processing delays 会增加,并产生 log retention risks。

Schema evolution 是另一个关键关注点。Producers 和 consumers 必须就兼容 schemas 达成一致,这通常通过 schema registry 并使用 Avro 或 Protobuf 等 formats 执行。

最后,streaming systems 支持通过 resetting offsets 进行 replay,这非常强大,但它要求 idempotent design,以防止 duplicate processing。

因此,stream ingestion 支持 continuous event-driven architectures,但为了在规模化下保持可靠,它要求仔细处理 delivery guarantees、ordering、schema management 和 operational monitoring。

实时处理的简单流程如下:

  1. Collect Data:来自 IoT sensors、apps 或 social feeds 等 sources。
  2. Ingest Data:使用 Apache Kafka 或 AWS Kinesis 等工具处理 high-speed streams。
  3. Process Streams:使用 Apache Flink 或 Spark 等 frameworks 即时转换和分析。
  4. Store Results:存入 in-memory databases,例如 Redis,以获得速度;或者按需存入 long-term storage。
  5. Analyze and Act:Dashboards 和 alerts 会立即显示 insights,因此 systems 可以自动触发 actions。

Use Case Example:Real-Time Product View Tracking

我们希望实时跟踪用户在 e-commerce website 上查看产品的行为。系统应做到:

  • 将 user activity data 发送到 Kafka topic。
  • 在 consumer side 实时读取该数据。
  • 打印或存储数据,用于进一步 analytics。

因此,你需要确保具备:

  • 本地或通过 Docker 运行的 Apache Kafka 和 Zookeeper。
  • Python Packages:kafka-pythonjson
  • 安装 Kafka Python client:pip install kafka-python

启动 Kafka 和 Zookeeper(Docker 示例)

docker run -d --name zookeeper -p 2181:2181 zookeeper

docker run -d --name kafka -p 9092:9092 --link zookeeper \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT \
confluentinc/cp-kafka

Step 1:Kafka Producer(模拟 User Activity)

# producer.py
from kafka import KafkaProducer
import json
import time
import random

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

product_ids = [101, 102, 103, 104]
user_ids = [1, 2, 3, 4, 5]

while True:
    event = {
        "user_id": random.choice(user_ids),
        "product_id": random.choice(product_ids),
        "event_type": "view_product",
        "timestamp": time.strftime('%Y-%m-%d %H:%M:%S')
    }
    producer.send('product_views', value=event)
    print(f"Sent: {event}")
    time.sleep(1)  # simulate real-time every second

Step 2:Kafka Consumer(读取 Real-Time Data)

# consumer.py
from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    'product_views',
    bootstrap_servers='localhost:9092',
    value_deserializer=lambda m: json.loads(m.decode('utf-8')),
    auto_offset_reset='earliest',
    enable_auto_commit=True,
    group_id='view-consumers'
)

print("Waiting for product view events…")

for msg in consumer:
    event = msg.value
    print(f"Received Event -> User {event['user_id']} viewed Product {event['product_id']} at {event['timestamp']}")

Expected Output

当你在两个 terminals 中运行 producer.pyconsumer.py 时,会看到如下输出:

Producer Output:

Sent: {'user_id': 3, 'product_id': 104, 'event_type': 'view_product', 'timestamp': '2025-03-31 14:55:01'}
Sent: {'user_id': 1, 'product_id': 101, 'event_type': 'view_product', 'timestamp': '2025-03-31 14:55:02'}
…

Consumer Output:

Received Event -> User 3 viewed Product 104 at 2025-03-31 14:55:01
Received Event -> User 1 viewed Product 101 at 2025-03-31 14:55:02

Tools for Real-Time Streaming

Real-time data streaming 依赖多类工具,它们在 ingestion、transport、processing、storage 和 monitoring 等不同阶段协同工作。以下是主要类别和每类常用工具。

Message Brokers / Data Transport

任何 streaming pipeline 的核心都是 message broker,也就是一个以低延迟和高吞吐将数据从 producers,例如 applications 或 sensors,移动到 consumers,例如 databases 或 processing engines 的系统。

ToolDescription
Apache Kafka行业标准 distributed streaming platform,高度 scalable 和 durable
Amazon KinesisAWS-managed service,类似 Kafka;与 AWS tools 集成良好
Google Pub/SubGoogle Cloud 的 messaging system,用于 real-time ingest 和 delivery
Apache PulsarKafka 替代方案,内置 multi-tenancy 和 topic-level scalability
RabbitMQLightweight message broker,更适合 lower-throughput use cases

表 4.2:Message Brokers 类型

Stream Processing Frameworks

当数据开始流动后,stream processing tools 会帮助实时转换、丰富和分析数据,通常具备 windowing、joins 或 aggregations 等能力。

ToolDescription
Apache Flink强大的 real-time engine,具备 low latency 和 exactly-once semantics
Apache Spark Structured Streaming使用 micro-batch model 的可扩展处理;适合 unified batch 和 stream pipelines
Kafka Streams内置于 Apache Kafka 的 lightweight stream processing library
ksqlDB用 SQL-like interface 处理 Kafka streams,无需写代码
Azure Stream AnalyticsAzure 上使用 SQL-like language 的 real-time processing

表 4.3:Stream Processing Frameworks

Ingestion and Routing

这些工具帮助从多样 sources 收集数据,并将其路由到合适的 stream platform,使 data ingestion pipelines 更易管理和扩展。

ToolDescription
Apache NiFiFlow-based UI tool,用于 ingest、transform 和 route data
LogstashELK stack 的一部分,适合 ingest logs 并 stream 到 Elasticsearch
Fluentd / Fluent BitLightweight log collector 和 forwarder
Debezium从 databases 实时捕获 change data(CDC),并通过 Kafka streaming

表 4.4:Ingestion and Routing Tools

Storage and Real-Time Databases

处理后的 real-time data 需要存入支持 fast querying 和 analytics 的系统。这些工具针对速度和高频数据访问进行了优化。

ToolDescription
RedisIn-memory store,用于 low-latency lookups 和 dashboards
Apache Druid针对 real-time analytics 和 OLAP queries 优化
ClickHouse快速 columnar database,支持 real-time ingestion 和 querying
Elasticsearch非常适合 full-text search 和 real-time dashboards,例如 Kibana

表 4.5:Real-Time Databases

Monitoring and Observability

为了确保 streaming pipeline 平稳运行,monitoring tools 会实时跟踪 performance、data lag、throughput、errors 和 system health。

ToolDescription
Prometheus and Grafana广泛用于监控 Kafka / Flink / Spark metrics 的组合
Kafka ManagerKafka cluster monitoring 和 management tools
Datadog支持 streaming 的 cloud-based observability platforms

表 4.6:Monitoring Tools

Real-time streaming 已成为现代数据架构不可或缺的一部分,因为它使企业能够从 reactive decision-making 转向 proactive decision-making。无论是监控 user activity、跟踪 logistics、检测 fraud,还是驱动 live dashboards,real-time data processing 都可以帮助组织保持响应性、敏捷性和竞争力。

借助广泛可用的工具,从 Kafka、Flink 到 Kinesis、Pub/Sub 等 cloud-native services,构建稳健且可扩展的 streaming pipelines 比以往更容易。然而,real-time streaming 也伴随挑战,例如确保 data quality、优雅处理 failures,以及在规模化下保持 low latency。

Change Data Capture

Change Data Capture(CDC)指识别并捕获 database 中数据 changes,然后将这些 changes 实时发送到下游 process 或 system 的过程。CDC 允许我们立即响应变化,并为 data lakes、data warehouses、analytics engines 或 microservices 带来 near real-time updates。

大多数 CDC systems 会直接读取 database 的 transaction log,例如 WAL 或 binlog,以捕获 inserts、updates 和 deletes,而无需扫描整张 tables。虽然这让 incremental synchronization 更高效,但 CDC 会引入必须仔细管理的 operational risks。

CDC pipelines 通常先从 existing data 的 full snapshot 开始,然后过渡到 streaming log-based changes。如果这个 handover 处理不当,可能产生 duplication 或 missed records。Source system 中的 schema changes 可能破坏 downstream consumers,除非 schema evolution 得到支持和协调。因此,delete events 会以 tombstones 的形式发出,并且必须被正确处理,以防止 stale records 在 analytical systems 中持续存在。

Transaction logs 有 retention limits。如果 CDC connector 落后,而 logs 已被清理,可能需要 full resnapshot。Connectors 也会维护 offsets,以跟踪它们在 log 中的位置;不正确的 offset handling 可能导致 duplicate processing 或 data gaps。由于大多数 CDC systems 保证 at-least-once delivery,下游系统必须具备 idempotent 能力。

CDC 对 incremental synchronization 非常强大,但它并不只是更快的 delta load。它是一种 replication mechanism,依赖 log retention policies、schema stability、offset integrity 和正确 delete handling。因此,如果没有周密设计和监控,CDC pipelines 可能会 drift out of sync 或 silent failure。

假设你有一张 customer_orders table,某人通过以下语句更新 order status:

UPDATE customer_orders
SET status = 'shipped'
WHERE order_id = 123;

CDC 不会同步整张 table,而是只捕获这次 change,并发送类似以下 event:

{
"operation": "update",
"table": "customer_orders",
"before": { "order_id": 123, "status": "processing" },
"after": { "order_id": 123, "status": "shipped" },
"timestamp": "2025-03-31T12:00:45Z"
}

最常见的 CDC tools 如下:

ToolDescription
DebeziumOpen-source CDC tool
AWS DMSAWS Data Migration Service
FivetranCommercial ETL / CDC Platform
Oracle GoldenGateEnterprise CDC solution for Oracle
StreamSets / Hevo / Airbyte带 CDC connectors 的 ETL platforms

表 4.7:Popular CDC Tools

使用 CDC tool 时,它会提供如下收益:

Real-Time Data Sync:以最小延迟保持 warehouse 或 analytics store 同步。

Lightweight and Efficient:避免 full table scans,减少 network traffic。

Auditability:捕获 before 和 after values,对 compliance 和 traceability 有用。

Trigger Downstream Actions:根据 changes 驱动 microservices 或 alerts。

Use Case Example:捕获 customer_orders Table 的 Changes

每当 PostgreSQL database 中新增、更新或删除 order 时,我们希望实时捕获该 change,并发送到 Kafka,使 downstream systems,例如 analytics 或 microservices,能够立即响应。

我们将使用以下工具:

  • PostgreSQL:Source database
  • Debezium:Open-source CDC tool
  • Kafka and Zookeeper:Message broker
  • Kafka Connect:承载 Debezium connector
  • Kafka Consumer(Python) :从 Kafka 读取 changes

Docker Compose Setup

创建名为 docker-compose.yml 的文件:

version: '2'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.2.1
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:7.2.1
    depends_on:
      - zookeeper
    environment:
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  postgres:
    image: debezium/postgres:13
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: inventory

  connect:
    image: debezium/connect:2.2
    depends_on:
      - kafka
      - postgres
    ports:
      - 8083:8083
    environment:
      BOOTSTRAP_SERVERS: kafka:9092
      GROUP_ID: 1
      CONFIG_STORAGE_TOPIC: my_connect_configs
      OFFSET_STORAGE_TOPIC: my_connect_offsets
      STATUS_STORAGE_TOPIC: my_connect_statuses
      CONNECT_KEY_CONVERTER: org.apache.kafka.connect.json.JsonConverter
      CONNECT_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
      CONNECT_REST_ADVERTISED_HOST_NAME: connect
      CONNECT_PLUGIN_PATH: /kafka/connect,/debezium-connector-postgres

Create Table and Insert Sample Data

Containers 启动后,进入 Postgres container:

docker exec -it <postgres_container_id> psql -U postgres -d inventory

创建 table 并添加一条 record:

CREATE TABLE customer_orders (
id SERIAL PRIMARY KEY,
customer_name VARCHAR(255),
product_id INT,
status VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO customer_orders (customer_name, product_id, status)
VALUES ('Alice', 101, 'processing');

Register the Debezium Connector

使用 curl 注册 PostgreSQL CDC connector:

curl -X POST http://localhost:8083/connectors \
-H "Content-Type: application/json" \
-d '{
"name": "pg-cdc-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"database.hostname": "postgres",
"database.port": "5432",
"database.user": "postgres",
"database.password": "postgres",
"database.dbname": "inventory",
"database.server.name": "pgcdc",
"table.include.list": "public.customer_orders",
"plugin.name": "pgoutput",
"slot.name": "debezium",
"publication.autocreate.mode": "filtered"
}
}'

Listen to Changes from Kafka Topic

Connector 会将 changes stream 到 Kafka topic:

pgcdc.public.customer_orders

使用简单 Python Kafka Consumer 监听:

from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    'pgcdc.public.customer_orders',
    bootstrap_servers='localhost:9092',
    value_deserializer=lambda m: json.loads(m.decode('utf-8')),
    auto_offset_reset='earliest',
    group_id='cdc-listener'
)

for msg in consumer:
    print(json.dumps(msg.value, indent=2))

Example Output

当你在 PostgreSQL 中更新 order status:

UPDATE customer_orders SET status = 'shipped' WHERE id = 1;

你会看到如下输出:

{
"before": {
"id": 1,
"customer_name": "Alice",
"product_id": 101,
"status": "processing",
"created_at": "2025-03-31T14:00:00Z"
},
"after": {
"id": 1,
"customer_name": "Alice",
"product_id": 101,
"status": "shipped",
"created_at": "2025-03-31T14:00:00Z"
},
"op": "u",
"ts_ms": 1741419600000
}

通过这个 setup,你已经构建了一个 real-time CDC pipeline,使用:

  • PostgreSQL 作为 source。
  • Debezium 捕获 changes。
  • Kafka 传输 events。
  • Python consumer 处理或显示 changes。

因此,Change Data Capture 是构建 low-latency、event-driven 和 efficient data systems 的关键能力。无论你是在同步系统、启用 real-time analytics,还是驱动 automation workflows,CDC 都提供了一种精确且可扩展的方法,让数据保持新鲜,而无需每次都做重型处理。此外,随着数据基础设施向 real-time needs 演进,CDC 正迅速成为必备能力。

API-Based Data Ingestion

Application Programming Interfaces(APIs)已经成为从外部或内部系统访问和摄入数据的最常见、最灵活方式之一。随着许多现代平台,从 social media 到 SaaS applications,都提供 RESTful APIs,通过 APIs 摄入数据已经成为数据工程中的一种基本模式。

API-based data ingestion 指通过 provider 暴露的 endpoints,以编程方式从 remote service 获取数据,通常通过 HTTP。返回数据通常是 JSON 或 XML 等格式,可以被 parsed、transformed,并加载到 data warehouse、lake 或 application 中,供进一步使用。

API data ingestion 涉及以下工具和要点:

Endpoint Access:定义 API URL 和支持的 parameters。

Authentication:许多 APIs 使用 tokens、OAuth 或 API keys。

Pagination:使用 cursors 或 offsets 处理多页 results。

Rate Limiting:遵守 API usage quotas,避免被 blocked。

Incremental Fetching:使用 updated_sincelast_modified 等 filters,避免重复拉取数据。

Error Handling:Retries、exponential backoff、logging 和 alerting。

示例:从 API 摄入 Click Events

假设你有一个 endpoint:

https://api.example.com/clicks?from=2025-03-30T00:00:00Z

Python Ingestion Script

import requests
import pandas as pd

API_URL = "https://api.example.com/clicks"
API_KEY = "your_api_key"

params = {
    "from": "2025-03-30T00:00:00Z"
}

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Accept": "application/json"
}

response = requests.get(API_URL, params=params, headers=headers)
data = response.json()

df = pd.DataFrame(data)
df.to_csv('clicks_2025_03_30.csv', index=False)

print("Data pulled and saved!")

你可以通过以下方式调度该 script:

  • 通过 cron jobs 每小时或每天运行。
  • 使用 Apache Airflow 的 SimpleHttpOperator 或 custom Python tasks。
  • 使用 NiFi 的 InvokeHTTP processor,如前面讨论过的方式。

API-based data ingestion 是任何现代数据栈中的必备技术。它提供 flexibility、timeliness,并可访问 real-time 或 third-party data,因此非常适合同步 metrics、丰富 internal data,或自动化从 SaaS platforms 拉取数据。

因此,借助合适工具和实践,你可以构建可靠且可扩展的 API connectors,用新鲜、有价值的 insights 驱动 pipelines。

Use Case:每天从 API 获取 clickstream data,并写入 PostgreSQL 表 click_events

请确保你具备:

  • 已安装 Airflow:pip install apache-airflow
  • 正在运行的 Airflow environment:airflow webserverairflow scheduler
  • API key,如果需要
  • PostgreSQL 正在运行,并且 Airflow 可访问它

我们将从类似如下 API 获取 clickstream data:

https://api.example.com/clicks?from=YYYY-MM-DD

随后将 response 存储到 PostgreSQL table 中。

创建目标表

在 Postgres DB 中运行:

CREATE TABLE IF NOT EXISTS click_events (
user_id TEXT,
product_id TEXT,
event_type TEXT,
timestamp
);

在 Airflow environment 中安装 Postgres client:

pip install psycopg2-binary

Airflow DAG Python File

from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.postgres.hooks.postgres import PostgresHook
from datetime import datetime, timedelta
import requests
import pandas as pd
from psycopg2.extras import execute_values

default_args = {
    "owner": "airflow",
    "retries": 2,
    "retry_delay": timedelta(minutes=5),
}

dag = DAG(
    dag_id="clickstream_api_to_postgres",
    default_args=default_args,
    start_date=datetime(2025, 3, 30),
    schedule="@daily",
    catchup=False,
    tags=["api", "postgres", "ingestion"],
)

def fetch_and_store_clickstream(ds, **kwargs):
    API_URL = "https://api.example.com/clicks"
    API_KEY = "your_api_key"  # Ideally store in Airflow Connections/Variables

    params = {"from": f"{ds}T00:00:00Z"}
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Accept": "application/json",
    }

    response = requests.get(API_URL, params=params, headers=headers, timeout=30)
    response.raise_for_status()

    data = response.json()

    if not data:
        print("No records returned from API.")
        return

    df = pd.DataFrame(data)

    records = list(
        df[["user_id", "product_id", "event_type", "timestamp"]]
        .itertuples(index=False, name=None)
    )

    postgres_hook = PostgresHook(postgres_conn_id="your_postgres_connection")
    conn = postgres_hook.get_conn()
    cursor = conn.cursor()

    insert_query = """
    INSERT INTO click_events (user_id, product_id, event_type, timestamp)
    VALUES %s
    """

    execute_values(cursor, insert_query, records)

    conn.commit()
    cursor.close()
    conn.close()

    print(f"Inserted {len(records)} records into Postgres.")

ingest_and_store_task = PythonOperator(
    task_id="fetch_and_store_clickstream",
    python_callable=fetch_and_store_clickstream,
    dag=dag,
)

因此,这个 DAG 会:

  • 通过 API 获取 clickstream data。
  • 将 JSON parse 成 DataFrame。
  • 将数据插入 PostgreSQL 中的 click_events table。
  • 每天运行,并自动处理 retries / failures。

因此,API-based data ingestion 是任何现代数据栈中的必备技术。它提供 flexibility、timeliness,并可访问 real-time 或 third-party data,因此非常适合同步 metrics、丰富 internal data,或自动化从 SaaS platforms 拉取数据。

因此,借助合适工具和实践,你可以构建可靠且可扩展的 API connectors,用新鲜、有价值的 insights 驱动 pipelines。

IoT and Sensor Data Ingestion

Internet of Things(IoT)devices 的爆发式增长,从 smartwatches 和 industrial sensors,到 connected cars 和 smart home devices,引入了一种新的数据类别:高 volume、高 velocity,并且通常 time-sensitive 的 sensor data。因此,高效摄入这类数据,对解锁 real-time insights、automation 和 responsive systems 至关重要。

IoT and sensor data ingestion 是收集物理设备生成的数据,例如 temperature sensors、motion detectors 或 GPS trackers,并将其传输到 data pipelines 中用于 storage、processing 和 analysis 的过程。Ingestion layer 充当 physical world 与 analytics 或 operational systems 之间的 gateway。

IoT Data 的特征如下:

FeatureDescription
High FrequencyDevices 每几毫秒或几秒发送一次数据
Time-Series数据按 time 索引,适合 trend analysis 和 monitoring
Small PayloadsLightweight messages,例如 10–50 bytes,包含少量 fields
Unstructured / Structured有些数据是 JSON-encoded,另一些是 binary
Edge-OriginatedDevices 可能位于 remote 或 offline environments

表 4.8:IoT Data 的特征

IoT Data 的 use cases 包括:

Industrial Automation:监控工厂中 machinery temperature、pressure 和 vibrations。

Fleet Tracking:流式传输 GPS coordinates 和 vehicle diagnostics。

Healthcare Monitoring:Wearables 跟踪 patient vitals,例如 heart rate 或 oxygen levels。

Agriculture:Sensors 测量 soil moisture、humidity 和 crop conditions。

Smart Homes:Thermostats、cameras 和 door sensors 生成 usage logs。

Real-World Architectural Concerns

设计 IoT ingestion pipelines 带来的挑战,远远不只是接收 messages。

Device Authentication and Identity

每个 device 在被允许发布数据之前,都必须能被唯一识别并完成认证。在大规模部署中,这通常包括:

  • X.509 Certificates 或 Token-Based Authentication
  • Device Registries
  • Rotating Credentials
  • Manufacturing 阶段的 Secure Provisioning

因此,如果没有强 authentication,ingestion endpoints 会容易受到 spoofed 或 malicious data injection。

Offline Buffering and Intermittent Connectivity

Devices 经常运行在连接不稳定的环境中。因此,ingestion design 必须考虑:

  • Local Device-Side Buffering
  • Store-and-Forward Mechanisms
  • Retry Strategies with Exponential Backoff
  • Sequence Numbering to Detect Gaps

系统必须假设数据可能 delayed、out of order,或者在重新连接后 burst 到达。

Duplicate Messages and Idempotency

Network retries 或 at-least-once delivery semantics 经常导致 duplicate events。因此,ingestion layer 应支持:

  • Message IDs 或 Sequence Numbers
  • Idempotent Writes
  • Stream 或 Storage 层面的 Deduplication Logic

如果不处理 duplicates,可能扭曲 metrics、触发 false alerts,或膨胀 downstream aggregates。

MQTT Quality of Service(QoS)Levels

MQTT 是常见 IoT protocol,它提供三种 Quality of Service(QoS)levels:

QoS 0:At most once delivery,没有 acknowledgment。

QoS 1:At least once delivery,可能出现 duplicates。

QoS 2:Exactly once delivery,但 overhead 更高。

所选择的 QoS level 会直接影响 reliability、latency 和 infrastructure load。更高 guarantees 会增加 network 和 broker complexity。因此,合适 level 取决于 telemetry 的 business criticality。

Edge Aggregation and Preprocessing

将每一条 raw reading 都发送到 central systems 往往不现实。Edge aggregation 可以通过以下方式降低 bandwidth usage 和 cost:

  • Filtering unnecessary signals
  • Aggregating metrics over windows
  • Compressing data
  • Performing anomaly detection locally

因此,edge processing 会降低 upstream volume,并在 connectivity disruptions 期间提升 system resilience。

Data Volume Realities

虽然单条 sensor message 很小,但规模会改变一切。假设部署 100,000 台设备,每台设备每秒发送一条 message,就会每天生成 86 亿条 messages。即使 payload 很轻,也会迅速转化为巨大的 storage、network 和 compute requirements。

因此,架构必须考虑:

  • Brokers 的 horizontal scaling,例如 Kafka clusters
  • Partitioning strategies
  • Time-based storage partitioning
  • Retention policies
  • Historical telemetry 的 cold storage tiering

因此,如果缺乏有意设计,IoT pipelines 可能压垮 infrastructure,或导致成本不可承受。

Use Case:Smart Thermostat Sensor Data Pipeline

一家 smart home company 希望监控部署在住宅和商业楼宇中的数千个 smart thermostats 的 temperature 和 humidity readings。目标是:

  • 实时跟踪 environmental conditions。
  • 检测异常 temperature fluctuations,例如 A/C failure。
  • 通过 predictive analytics 优化 energy usage。
  • 为 users 和 facility managers 提供 real-time dashboards。

下面按组件拆解解决方案。

Smart Thermostat Devices

每台 thermostat device,通常基于 ESP32 或 Raspberry Pi Zero 等 lightweight hardware 构建,会采集其环境中的 temperature 和 humidity data。随后,这些 devices 会被编程为使用 MQTT protocol 每隔几秒发送 sensor readings。MQTT 是低功耗 IoT communication 的热门选择。

Message payload 通常很小,并且采用 JSON format 结构化,例如:

{
"device_id": "thermo-0021",
"location": "office_12B",
"temperature": 26.3,
"humidity": 48.2,
"timestamp": "2025-03-31T14:05:00Z"
}

这确保数据不仅 compact,而且 human-readable,并易于下游处理。

MQTT Broker

所有 thermostat devices 都将数据发布到 MQTT broker。MQTT broker 充当 incoming sensor messages 的 central hub。Broker 会将 messages 组织到 topics 中,例如:

sensors/temperature/thermo-0021

MQTT broker 针对数千个 concurrent device connections 进行了优化,并能高效处理 message delivery。为了将这些数据移入 analytics pipeline,messages 会从 broker 转发到 Kafka topic。可以使用 Kafka MQTT Source Connector,或使用 Python 的 paho-mqtt 等 libraries 构建 lightweight bridge。

Apache Kafka

一旦 sensor data 到达 Kafka,就会被发布到 topic,例如:

temperature_readings

Kafka 在这里扮演关键角色,它是一个 high-throughput、fault-tolerant message broker,可以 buffer 并解耦 data producers,也就是 thermostats,与 consumers,例如 analytics engines。

Kafka 还确保 message durability,这意味着即使 downstream systems 暂时离线,数据也不会丢失。此外,Kafka 的 replayability 允许你为了 backfilling 或 debugging 而重新处理数据。

Apache Flink(或 Spark Structured Streaming)

这是 streaming system 的“大脑”。Apache Flink 等工具会实时处理 incoming data,使你可以即时应用 aggregations、filters 和 anomaly detection 等逻辑。

例如,Flink 可以:

  • 按 device 每分钟聚合 average temperature 和 humidity。
  • 检测 temperature 是否上升过快,或超过定义 threshold。
  • 即时生成 metrics 和 alerts。

因此,一个简单 use case 可能是计算每个 thermostat 的 1-minute rolling average temperature,或将超过 35°C 的值标记为潜在 unsafe。

Time-Series Storage(Druid、TimescaleDB、InfluxDB)

处理后的数据会写入 time-series database,这类数据库针对存储和查询 time-stamped data 进行了优化。

Apache Druid 非常适合 real-time OLAP analytics 和 large-scale interactive dashboards。

TimescaleDB 基于 PostgreSQL,当你需要 SQL compatibility 时很适合。

InfluxDB 是 developer-friendly 的 time-series database,尤其适合 DevOps 和 IoT workloads。

这些系统支持快速检索 historical data,用于 charting、analysis,甚至作为 ML models 的输入。

Dashboards and Alerts(Grafana、Superset)

最后,processed data 会使用 Grafana 或 Apache Superset 等 dashboarding tools 可视化。这些工具可以构建 real-time charts、heatmaps 和 anomaly dashboards。Facility managers 可以:

  • 监控 buildings 中各房间的 temperatures。
  • 发现 HVAC systems 中的问题。
  • 当 thresholds 被突破时,通过 email 或 Slack 接收 real-time alerts。

这种端到端可见性将 raw sensor data 转化为 actionable insights。

因此,通过将这些组件组合成 cohesive pipeline,企业可以实时监控 environmental conditions、立即响应 anomalies,并主动优化 energy usage。这是 IoT、streaming 和 analytics 如何结合起来,创建更智能、更响应式系统的典型示例。

IoT and sensor data ingestion 是现代 connected systems 的基石,从工厂中的 predictive maintenance,到 smart homes 中的 personalized experiences 都离不开它。借助正确 architecture、protocols 和 tooling,组织可以将 raw sensor signals 转化为 actionable intelligence,从而驱动 automation、alerts 和 real-time analytics。

结论

本章考察了现代 data pipelines 中使用的主要 data ingestion patterns,包括 batch processing、real-time streaming、API-based ingestion、IoT / sensor feeds 和 Change Data Capture(CDC)。每种 pattern 都对应 volume、latency、operational complexity 和 governance requirements 的不同组合。

当数据周期性到达,并且 latency requirements 以小时计时,batch ingestion 是合适的。Real-time streaming 更适合 low-latency use cases,在这些场景中 events 必须被连续处理。当 downstream systems 需要反映 database changes,而不想重复加载整张 tables 时,CDC 很有用。当需要与暴露受控、request-driven access 的 external platforms 集成时,API-based ingestion 通常不可避免。在处理必须高效处理和存储的 high-velocity telemetry data 时,IoT 和 sensor ingestion 就变得必要。

因此,ingestion pattern 的选择应由 constraints 驱动,而不是由偏好驱动。如果 latency tolerance 高且 cost simplicity 重要,batch 通常足够。如果 decisions 依赖 immediate event availability,streaming 更合适。如果需求是将 operational databases 与 analytical systems 进行 incremental synchronization,CDC 提供了一种高效机制。如果数据由 third-party systems 控制,API ingestion 不可避免。因此,在处理 continuous、high-frequency device data 时,带 time-series storage 的 streaming architectures 变得必要。

因此,关键结论是:ingestion design 是一项 constraint-matching exercise。Volume、latency expectations、SLA commitments、reliability guarantees、governance requirements 和 schema evolution,都应该指导选择。

下一章中,我们将从 ingestion 转向 storage,考察不同 storage architectures 如何支持 analytics、scalability 和 long-term data management。