Trade-offs Between Batch and Streaming Processing Architectures in Data Engineering

Batch processing excels at high-volume historical analytics with simpler operations and lower costs, while streaming processing delivers sub-second latency for real-time applications at the expense of increased complexity and infrastructure overhead.

Modern data platforms must decide whether to process data as bounded collections or continuous flows. Understanding the trade-offs between batch and streaming processing architectures is essential for building cost-effective, scalable data pipelines. The Data Engineering Zoomcamp repository provides hands-on implementations of both paradigms, demonstrating how Apache Spark handles periodic workloads while Kafka and Faust manage real-time event streams.

Fundamentals of Batch Processing Architectures

Batch processing handles data in discrete, bounded chunks scheduled at intervals (hourly, daily, or weekly). This approach dominates historical analytics and large-scale ETL workflows.

Latency and Throughput Characteristics

Batch systems process data after accumulation periods, resulting in end-to-end latency measured in minutes to hours. However, this delay enables high-throughput processing of massive datasets using distributed frameworks like Apache Spark.

In 06-batch/code/06_spark_sql.py, the Data Engineering Zoomcamp implements a classic batch pattern:


# 06-batch/code/06_spark_sql.py

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("batch-demo").getOrCreate()

# Read CSV (bounded source)

df = spark.read.csv("data/yellow_tripdata_2022-01.csv", header=True, inferSchema=True)

# Simple transformation

df_filtered = df.filter(df["passenger_count"] > 0)

# Write to Parquet (data lake)

df_filtered.write.mode("overwrite").parquet("output/yellow_tripdata_parquet")

This example demonstrates reading static CSV files, applying filters, and writing to Parquet—operations that execute once per scheduled job.

Complexity and Operational Simplicity

Batch jobs offer simpler programming models because they process finite datasets with clear start and end boundaries. Debugging is straightforward since developers can replay entire datasets, and Spark's built-in retry mechanisms provide robust fault tolerance through straightforward checkpointing.

The repository also demonstrates BigQuery integration in 06-batch/code/06_spark_sql_big_query.py, showing how batch results load efficiently into analytics warehouses for reporting.

Cost Structure

Batch processing follows a bursty cost model—clusters provision only during execution, making idle periods inexpensive. However, large batches require significant compute resources during processing windows, creating peak usage spikes.

Real-Time Streaming Processing Architectures

Streaming architectures process unbounded data flows continuously, enabling immediate insights and reactive systems.

Latency and Event Processing

Streaming systems handle individual events as they arrive (or in micro-batches), achieving sub-second latency essential for fraud detection, real-time dashboards, and alerting systems.

The Zoomcamp repository illustrates this in cohorts/2023/week_6_stream_processing/streaming_confluent.py, which implements Structured Streaming with Confluent Kafka for near real-time analytics.

Stateful Operations and Fault Tolerance

Unlike stateless batch jobs, streaming requires managing stateful operators, windowing, and exactly-once semantics. Frameworks like Faust, Flink, and Kafka Streams maintain state stores and checkpoint offsets to ensure data consistency across failures.

The Faust-based implementation in 07-streaming/extras/python/streams-example/faust/stream_count_vendor_trips.py demonstrates stateful aggregation:


# 07-streaming/extras/python/streams-example/faust/stream_count_vendor_trips.py

import faust

app = faust.App("taxi-aggregator", broker="kafka://localhost:9092")

class Ride(faust.Record):
    vendor_id: str
    trip_distance: float

rides = app.topic("taxi_rides", value_type=Ride)

# Table to keep per-vendor total distance

vendor_distance = app.Table("vendor_distance", default=float)

@app.agent(rides)
async def aggregate(stream):
    async for ride in stream:
        vendor_distance[ride.vendor_id] += ride.trip_distance

if __name__ == "__main__":
    app.main()

This agent consumes from Kafka topics and maintains persistent tables that survive restarts when properly configured with checkpointing.

Infrastructure Requirements and Guarantees

Streaming demands continuously running compute resources and persistent messaging infrastructure (Kafka or Redpanda), resulting in higher baseline costs. Offset management becomes critical, as shown in 07-streaming/workshop/src/consumers/consumer.py, where careful tracking ensures exactly-once processing guarantees through transactional writes.

Producer implementations in 07-streaming/workshop/src/producers/producer.py demonstrate the continuous data injection pattern:


# 07-streaming/workshop/src/producers/producer.py

import json, time
from confluent_kafka import Producer

p = Producer({"bootstrap.servers": "localhost:9092"})

def delivery_report(err, msg):
    if err is not None:
        print(f"Delivery failed: {err}")

while True:
    record = {"user_id": 123, "event": "click", "ts": time.time()}
    p.produce("events", json.dumps(record).encode("utf-8"), callback=delivery_report)
    p.poll(0)
    time.sleep(0.5)

Architectural Trade-offs and Decision Framework

Choosing between batch and streaming involves balancing latency requirements against operational complexity and cost constraints.

Select Batch Processing When:

  • Business requirements tolerate minutes-to-hours delay
  • Transformations require full-dataset joins or complex aggregations across historical data
  • Operational simplicity and lower infrastructure overhead are priorities
  • Workloads follow predictable schedules (nightly ETL, hourly syncs)

Select Streaming Processing When:

  • Immediate insight or automated action is required (anomaly detection, inventory alerts)
  • Data volume is unbounded and unpredictable
  • Applications require continuously updated materialized views or real-time feature stores
  • Sub-second latency directly impacts business outcomes

Hybrid Architectures:

Production systems often implement Lambda architectures, combining both paradigms. A streaming layer handles low-latency alerting while a batch layer performs comprehensive, heavyweight analytics on historical data. This approach mitigates individual weaknesses while leveraging strengths of each processing model.

Summary

  • Batch processing provides simpler operational models, lower baseline costs, and efficient handling of large historical datasets, but introduces latency measured in minutes or hours.
  • Streaming processing delivers sub-second latency and handles unbounded data flows, but requires complex state management, exactly-once semantics, and continuously running infrastructure.
  • The Data Engineering Zoomcamp demonstrates batch implementations in 06-batch/code/ using Spark SQL, while streaming examples in 07-streaming/ showcase Kafka producers and Faust consumers.
  • Most production environments benefit from hybrid architectures that route real-time events to streaming pipelines while processing bulk historical data through batch systems.

Frequently Asked Questions

What is the main latency difference between batch and streaming?

Batch processing accumulates data over defined intervals (hours or days) before processing, resulting in end-to-end latency of minutes to hours. Streaming processing handles events as they arrive, achieving near real-time latency typically measured in seconds or milliseconds, enabling immediate dashboards and alerts.

Is streaming processing always more expensive than batch?

Generally yes, due to continuously running compute resources and persistent messaging infrastructure like Kafka clusters. However, batch processing can incur high peak costs during large-scale transformations. The optimal cost structure depends on data volume, processing frequency, and latency requirements.

Can I combine batch and streaming in the same pipeline?

Absolutely. This approach, known as the Lambda architecture, uses a speed layer for real-time streaming analytics and a batch layer for comprehensive historical processing. The Data Engineering Zoomcamp repository provides codebases for both paradigms, allowing practitioners to implement complementary systems serving different use cases.

Which processing style should beginners learn first in the Data Engineering Zoomcamp?

Start with batch processing using Spark SQL (06-batch/code/06_spark_sql.py), as it offers a simpler mental model with bounded datasets and straightforward debugging. Once comfortable with data transformations and fault tolerance in batch mode, progress to streaming concepts using the Faust and Kafka examples in 07-streaming/ to understand stateful operations and exactly-once semantics.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →