Spark Streaming vs Structured Streaming

DStreams Compared

Spark Streaming (DStreams) is the original RDD-based micro-batch API, and Spark's documentation calls it a legacy project with no more updates. Structured Streaming is the current engine: you write a DataFrame query and the Spark SQL engine runs it incrementally. It supports event-time windows and watermarks, and it gives end-to-end exactly-once for replayable sources and idempotent sinks.

Last updated: Proudly published by: Jeff Wahl7 min read

Spark Streaming vs Structured Streaming in short

Spark Streaming, also called DStreams, is the first streaming API in Spark: it cuts a stream into small batches and hands you each one as an RDD. Structured Streaming is its replacement. You write a DataFrame query as if the data were a static table, and the Spark SQL engine runs it incrementally as new rows arrive.

The Spark streaming programming guide calls Spark Streaming the previous generation of the engine, says there are no longer updates to it and calls it a legacy project. It tells you to use Structured Streaming for your streaming applications and pipelines. For a new job the choice is made. The differences still matter if you maintain a DStreams job or plan to migrate one, and an interviewer may ask about them.

DStreams vs Structured Streaming at a glance

DStreams
  • Core abstractionDStream of RDDs
  • You writeRDD operations
  • Windows are set inBatch interval multiples
  • DeliveryAt-least-once output
  • Time between runsBatch interval
  • Status in the docsLegacy, no updates
Structured Streaming
  • Core abstractionUnbounded DataFrame
  • You writeDataFrame and SQL
  • Windows are set inEvent time
  • DeliveryExactly-once, replayable
  • Time between runsTrigger
  • Status in the docsCurrent engine

How DStreams and Structured Streaming model a stream

A DStream is a sequence of RDDs. The batch interval you pass to StreamingContext decides how much of the stream goes into each RDD, and every operation you write is a transformation on those RDDs. With a 2 second interval, your code sees 1 RDD every 2 seconds and has no table to query.

2 models side by side. DStreams cuts the stream into one RDD per 2 second batch. Structured Streaming appends new rows to an input table and one query updates a result table2 models side by side. DStreams cuts the stream into one RDD per 2 second batch. Structured Streaming appends new rows to an input table and one query updates a result table

Structured Streaming treats the live stream as a table that is continuously appended. The query you write on it is the same as on a static DataFrame, and Spark runs it as an incremental query on the unbounded input table. The guide notes that Structured Streaming doesn't materialize the entire table. It reads the latest available data and updates the result, then discards the source data. Each trigger appends new rows to the input table and updates the result table.

The practical consequence is where the optimizer sits. A DStream program is a chain of RDD functions that Spark runs as you wrote them. A Structured Streaming query goes through the same optimized Spark SQL engine as a batch DataFrame job.

The same word count in DStreams and Structured Streaming

PYDStreams
from pyspark import SparkContext
from pyspark.streaming import StreamingContext

sc = SparkContext("local[2]", "NetworkWordCount")
ssc = StreamingContext(sc, 2)  # batch interval in seconds

lines = ssc.socketTextStream("localhost", 9999)
words = lines.flatMap(lambda line: line.split(" "))
pairs = words.map(lambda word: (word, 1))
counts = pairs.reduceByKey(lambda x, y: x + y)  # counts per batch only
counts.pprint()

ssc.start()
ssc.awaitTermination()

State, aggregations and joins

The 2 word count listings look alike but count different things. reduceByKey on a DStream counts within each batch. A running total across batches needs updateStateByKey, which requires checkpointing, runs its update function for every key in every batch and drops a key when the function returns None. In Structured Streaming groupBy().count() is already a running count, and Spark keeps its state in the checkpoint location.

For custom per-key state, Structured Streaming has mapGroupsWithState and flatMapGroupsWithState since Spark 2.2. Since Spark 4.0 the guide encourages the newer transformWithState operator for complex stateful applications. Stream-stream joins exist since Spark 2.3, and for outer joins you must specify a watermark and event-time constraints.

Structured Streaming also has limits you will hit. On streaming Datasets, limit isn't supported, nor is taking the first N rows, and distinct isn't supported either. Sorting works only after an aggregation in Complete output mode. Actions such as count() and show() do not apply: you write to a sink, such as the console sink, instead.

Prepare for the interview
01 / Open invite
02min.

Know Spark Streaming vs Structured Streaming the way the interviewer who asks it knows it.

a Spark Streaming vs Structured Streaming query, the same shape a screen would give you.
The diff against expected. Where ties broke. What you missed.
sandbox
1SELECT user_id,
2 COUNT(*) AS sessions
3FROM events
4WHERE ts >= NOW() - INTERVAL '7 day'
5
Execute your solution0.4s avg.

Event time, windows and late data

A DStream window is defined by a window length and a slide interval, and both must be multiples of the batch interval. Windows are cut from when batches were processed. Event time is the time embedded in the data itself, and in Structured Streaming a window aggregation is a grouping on the event-time column, where each window is a group and a row can belong to several windows.

Late data is where that matters. Since Spark 2.1 Structured Streaming supports watermarks, which let you state how late data may be and let the engine clean up old state. The guarantee runs in one direction: data less than the threshold behind the latest event time is guaranteed to be aggregated, and data later than that may or may not be.

How a watermark closes a window

Formula
state is dropped once (max event time seen - late threshold) > window end
max event time seen
The latest event time in the data the engine has processed
late threshold
The delay you allow, set with withWatermark
window end
T, the end of the window being aggregated
10 minute threshold, window ending 12:10
Max event time 12:2012:10
Max event time 12:2112:11

Delivery guarantees: exactly-once in each API

Both APIs can reach exactly-once, by different routes. With DStreams, the guarantee depends on the source and the output. Reliable receivers acknowledge a source only after the data is replicated, unreliable receivers can lose data on failure, and the Kafka Direct API, introduced in Spark 1.3, receives Kafka data exactly once. Output operations such as foreachRDD are at-least-once, so exactly-once end to end needs an idempotent update or a transaction identified by batch time and partition index.

Structured Streaming builds the guarantee into the engine. Sources are assumed to have offsets, the engine records the offset range of each trigger with checkpointing and write-ahead logs, and the sinks are designed to be idempotent for reprocessing. With replayable sources and idempotent sinks, the Structured Streaming guide states end-to-end exactly-once semantics under any failure. The exception you own is foreachBatch, which is at-least-once by default: use the batchId it passes you to deduplicate.

Batch interval vs trigger, and how fast each can go

A DStream job is stable only while batch processing time stays below the batch interval. Take a 2 second interval and batches that need 3 seconds: each batch finishes 1 second later than the next one is due, so after 10 batches the job is 10 seconds behind and still falling back.

Structured Streaming handles overruns by rule. With no trigger set, the next micro-batch starts as soon as the previous one completes. With a fixed interval, if a batch overruns, the next starts immediately instead of waiting for the next boundary, and if no new data is available no batch runs. The default micro-batch engine reaches end-to-end latencies of about 100 ms at best. Continuous Processing, experimental since Spark 2.3, aims at about 1 ms with at-least-once guarantees for map-like queries. Real-time Mode, introduced in Spark 4.1.0, targets millisecond latency with the same APIs, for stateless queries in 4.1.0 and some stateful ones from 4.3.0.

A trigger that DStreams never had is availableNow: it processes everything available, possibly in several micro-batches, then stops. It suits jobs that start a cluster periodically, process what arrived since the last run and shut down. The older one-time trigger is deprecated in its favor.

Moving from DStreams to Structured Streaming

DStreams pieceStructured Streaming replacementWhat changes
`StreamingContext``SparkSession.readStream`The entry point is the SparkSession you already use for batch.
Batch intervalTriggerRun as fast as possible, at a fixed interval, or `availableNow`.
RDD `map` and `reduceByKey`DataFrame `select` and `groupBy`Logic is rewritten against columns and goes through the SQL engine.
`window` on batch intervals`window` on an event-time columnWindows follow the time in the data and can use a watermark.
`updateStateByKey`Aggregation or `transformWithState`State lives in the checkpoint location, and you set its cleanup with a watermark.
`foreachRDD``foreachBatch` or a sinkWrites are at-least-once, so deduplicate with the batch id.
Checkpoint directory`checkpointLocation` optionSet per query, on an HDFS-compatible fault-tolerant file system.

What to check in an existing DStreams job

A DStreams job in production usually carries more than a chain of transformations. Start with the state: every updateStateByKey or windowed reduce with an inverse function depends on checkpointing, and each one needs a Structured Streaming aggregation or stateful operator with a watermark that bounds how long state lives.

Then read the output. foreachRDD is at-least-once, so the job already carries an idempotent write or a transactional commit tagged with batch time and partition index. In Structured Streaming the equivalent is the built-in idempotent sinks, or foreachBatch with the batchId as the deduplication key. Last, find the clocks. A DStreams window counts time by when batches ran, so a rewrite to event-time windows changes which window a late record lands in, and the new output should be compared with the old before the old job is switched off.

How this comparison comes up in data engineering interviews

Interviewers use the pair to see whether you know what a streaming engine guarantees. The strong answer names the model (RDD micro-batches vs an incrementally maintained query), says where event time and watermarks come in, and says what you must add yourself for exactly-once output. Be ready to say which trigger fits a job and what happens when a batch overruns.

Practice the Spark side with the PySpark interview questions and the PySpark practice problems. The design side, such as choosing batch or streaming for a pipeline, is covered by streaming system design interview questions and the data engineer interview prep hub.

Spark Streaming vs Structured Streaming FAQ

What is the difference between Spark Streaming and Structured Streaming?+
Spark Streaming is the original streaming API, built on DStreams, which are sequences of RDDs cut by a batch interval. Structured Streaming is the newer engine on the Spark SQL engine, where you write a DataFrame query and Spark runs it incrementally over data that keeps arriving. The Spark documentation calls Spark Streaming a legacy project with no more updates and tells you to use Structured Streaming.
What is a DStream?+
A DStream, or discretized stream, is the abstraction of Spark Streaming: a continuous sequence of RDDs, 1 for each batch interval. You transform it with RDD-style operations such as map and reduceByKey, or with window operations.
Is Spark Streaming deprecated?+
The Spark documentation describes Spark Streaming as the previous generation of the streaming engine, says there are no longer updates to it and calls it a legacy project. It recommends Structured Streaming for streaming applications and pipelines.
Is Structured Streaming real streaming or micro-batch?+
By default it is micro-batch: the engine processes the stream as a series of small batch jobs, with end-to-end latencies as low as 100 ms. Continuous Processing, introduced in Spark 2.3 and still experimental, targets about 1 ms with at-least-once guarantees for a limited set of queries. Real-time Mode, introduced in Spark 4.1.0, targets millisecond latency with the same Structured Streaming APIs.
Does Structured Streaming support exactly-once processing?+
Yes, end to end, when the source is replayable and the sink is idempotent. The engine records the offset range of each trigger in checkpoints and write-ahead logs. Continuous Processing gives at-least-once instead, and foreachBatch writes are at-least-once unless you deduplicate with the batch id.
Can I use the same code for batch and streaming?+
Largely yes. The query on a streaming DataFrame is written the way it would be on a static one, and the Spark documentation shows the word count query as exactly the same. Some operations differ on streams: limit and distinct are not supported, and neither is sorting outside Complete mode after an aggregation.
Should I migrate DStreams jobs to Structured Streaming?+
The documentation recommends Structured Streaming for streaming applications and pipelines, and DStreams get no updates. Migration means rewriting RDD operations as DataFrame operations, replacing the batch interval with a trigger, and replacing per-key state code with aggregations or stateful operators.
02 / Why practice

The candidate who gets the offer

  1. 01

    Reading a solution is not the same as writing one

    Every engineer who has frozen on a query they had read a dozen times knows the gap. The only preparation that closes it is producing the answer yourself, under time, before the interview does it for you

  2. 02

    76% of hiring managers reject on the coding task, not the resume

    From HackerRank's 2024 Developer Skills Report. Candidates who look strong on paper still fail the live screen if they haven't done timed, executable practice

  3. 03

    Spark rounds are diagnosis rounds

    Skew, shuffles, broadcast judgment, partition pruning, exactly-once streaming. Reading the task table and naming the failure class is the signal, not the API

Related guides