home
diamond Go Premium
Data Engineering Path  ·  PySpark

Caching & Persistence

In large-scale ETL pipelines, you often need to reuse an intermediate DataFrame multiple times (e.g. performing multiple aggregates, running iterative machine learning, or creating different branch tables). By default, Spark's lazy evaluation will re-compute that entire DataFrame from scratch every single time an action is triggered.

To avoid this massive re-computation penalty, you can cache the computed rows directly in memory.


Caching vs. Persistence

Spark provides two methods to store data in memory:

  1. cache(): A quick shortcut that uses the default storage level:
    • For RDDs: MEMORY_ONLY (unserialized in RAM).
    • For DataFrames: MEMORY_AND_DISK (unserialized in RAM, spills to disk when memory overflows).
  2. persist(storageLevel): The complete, professional API that allows you to specify a custom Storage Level depending on your memory limits.

Storage Levels Reference

Import pyspark.StorageLevel to configure persistence:

Storage Level Serialized? RAM or Disk? Description
MEMORY_ONLY No RAM Only Fast execution, high memory footprint. If a partition doesn't fit in RAM, it is recomputed on the fly.
MEMORY_ONLY_SER Yes (Binary) RAM Only Serializes rows into compact byte arrays. Slower to read (requires CPU deserialization), but saves up to 70% RAM space.
MEMORY_AND_DISK No RAM + Disk Keeps partitions in memory. Spills overflowing partitions directly to local executor disks.
MEMORY_AND_DISK_SER Yes RAM + Disk Serializes data and spills overflowing partitions to disk.
DISK_ONLY Yes Disk Only Bypasses RAM completely, storing data on executor disks.

PySpark Code Example: Caching & Eviction

Here is a complete script demonstrating DataFrame persistence, execution timing comparisons, and manual memory freeing (unpersist):

import time
from pyspark.sql import SparkSession
from pyspark import StorageLevel

# 1. Setup Spark
spark = SparkSession.builder \
    .appName("Caching and Persistence") \
    .master("local[*]") \
    .getOrCreate()

# 2. Load and create a large dummy dataset
# We create 10,000,000 integers to observe memory speeds
df = spark.range(1, 10000000).withColumn("value", F.col("id") * 2)

# 3. Persist DataFrame in memory using Serialization (saves memory space)
df.persist(StorageLevel.MEMORY_AND_DISK_SER)

# 4. Trigger Action A (This builds the cache - takes a few seconds)
start_time = time.time()
count_a = df.count()
print(f"Action A (Cache build) - Count: {count_a}, Time taken: {time.time() - start_time:.2f} seconds")

# 5. Trigger Action B (Reads directly from memory cache - extremely fast!)
start_time = time.time()
count_b = df.count()
print(f"Action B (From Cache) - Count: {count_b}, Time taken: {time.time() - start_time:.2f} seconds")

# 6. Release memory manually (Production Best Practice!)
# This prevents memory leaks and ensures subsequent steps have enough heap space
df.unpersist()

Production Best Practices & Pitfalls

  • Do Not Cache Everything: Caching consumes executor RAM. If you cache too many DataFrames, Spark will run out of memory, causing executor GC pauses, Out Of Memory (OOM) errors, or excessive disk spelling.
  • Always call unpersist(): As soon as you are finished with the cached DataFrame, call unpersist() to free up executor heap space.
Find this content helpful? ☕ Buy me a coffee

Entity Details

Create New Item

celebration
Enjoying the free content?

Create a free account to track your progress and save your place.

Create Free Account
help

Submit Technical Query

Have a question or run into an issue? Describe it below, upload an optional screenshot, and our engineering team will answer it!

image Attach image (optional)

Submit Feedback

build Free Developer Utility Free Tool
gavel

Privacy & Legal Disclaimer

1. Client-Side Browser Processing

All utility tools on DeepEngineerHub (including Image to PDF, Text Formatters, JSON Converters, and Encryptors) execute 100% locally within your client browser using WebAssembly and JavaScript. No uploaded images, text, or documents are transmitted, collected, or stored on remote servers.

2. Limitation of Liability ("As-Is" Provision)

Tools and services are provided free of charge for convenience and educational purposes "as-is" without warranties of any kind. DeepEngineerHub shall not be held liable for any data loss, formatting inconsistencies, or indirect damages resulting from tool usage.

3. Open Source & Third-Party Software

Certain utilities utilize open-source client libraries (such as jsPDF, Mermaid.js, Pyodide) licensed under MIT, Apache, or BSD open licenses. All intellectual property remains with their respective copyright holders.