Spill

A spill is an engine writing intermediate data to local disk because it does not fit in the memory allotted to the operation, then reading it back to finish the work. Sorting, grouping, joining, and shuffling all build state that grows with the data, and when that state exceeds the budget, spilling is what happens instead of failing.

Where the budget comes from

In Spark, the memory available for execution and storage is a documented fraction of the heap: spark.memory.fraction defaults to 0.6 of heap space minus 300 MB, and the documentation notes that lowering it means “spills and cached data eviction occur” more frequently. Within that region, spark.memory.storageFraction protects cached blocks from eviction, and raising it means “less working memory may be available to execution and tasks may spill to disk more often.” Both defaults come with an explicit recommendation to leave them alone.

Spark’s shuffle behaves the same way on the map side: tasks organize records in memory and, when capacity is exceeded, sort and write them to disk. Other engines implement the same idea with their own knobs — DuckDB, for instance, spills to a temporary directory whose location is configurable.

What it costs, and what it saves

  • It saves the query. Without spilling, an operation that exceeds memory fails outright. This is the same mechanism that lets a single machine process datasets larger than its RAM — see out-of-core processing.
  • It changes the speed class. The step now runs at the speed of the temporary storage, writing data out and reading it back rather than keeping it in memory.
  • It adds a capacity requirement. The temporary directory needs room. A job that spills tens of gigabytes per executor can fail on disk space rather than on memory, which reads as a confusing error at first.

Reading it as a signal

Spill volume per task is one of the more useful numbers in a job’s metrics, because it distinguishes two problems that look alike from the outside:

ObservationLikely readingFirst move
Every task spills a similar amountEach task is handling more data than its memory allowsMore partitions, so each task holds less; or more memory per executor
One task spills enormously, the rest not at allSkew — the partitioning, not the memory, is the problemFix the key distribution rather than the memory setting
Spill appears only after a code changeA new wide operation, a cached dataset holding memory, or a lost broadcastCompare the plans before and after

The order that usually works: reduce the data per task, remove caching that is not paying for itself, and only then raise memory — because a larger container hides the cause without removing it. How spill fits with skew, file sizes, and resource allocation is worked through in Why More Executors Stopped Helping.

References: Apache Spark documentation, Configuration; Apache Spark documentation, RDD Programming Guide; DuckDB Documentation, How to tune workloads.


Discover more from Insightful Data Lab

Subscribe to get the latest posts sent to your email.

Similar Posts

Questions, corrections, or additional insights?

This site uses Akismet to reduce spam. Learn how your comment data is processed.