advanced35-45 minutesLesson 6 of 10

Cluster Configuration

Configure Spark clusters: spark.sql.shuffle.partitions, spark.executor.memory, dynamic allocation, and resource optimization

Cluster Configuration

Proper Spark configuration is essential for performance, stability, and cost efficiency. This lesson covers the most critical configuration parameters and how to tune them.

Configuration Priority

Spark configuration follows a hierarchy:

PrioritySourceOverrides
1 (Highest)spark-submit --confAll below
2SparkSession.conf.set()Config file defaults
3spark-defaults.confHardcoded defaults
4 (Lowest)Hardcoded defaults

Core Resource Configuration

Executor Resources

python
from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("OptimizedApp") \ .config("spark.executor.memory", "8g") \ .config("spark.executor.cores", "4") \ .config("spark.executor.instances", "10") \ .config("spark.driver.memory", "4g") \ .getOrCreate()

Resource Calculation Formula

Total cores = executor.instances × executor.cores Total memory = executor.instances × executor.memory Example: 10 executors × 4 cores = 40 cores 10 executors × 8g = 80g total memory Overhead: 8g × 0.1 = 0.8g per executor (spark.executor.memoryOverhead)
ℹ️Note

Leave 1-2 cores per node for OS and Hadoop services. For YARN, container overhead reduces available memory. Set spark.executor.memoryOverhead to 10-15% of executor memory for JVM overhead.

Memory Breakdown

python
# Default memory allocation within an executor # spark.executor.memory = 8g # spark.memory.fraction = 0.6 (60% for execution + storage) # spark.memory.storageFraction = 0.5 (50% of unified for storage) # # Execution: 8g × 0.6 × 0.5 = 2.4g # Storage: 8g × 0.6 × 0.5 = 2.4g (can borrow from execution) # Reserved: 8g × 0.4 = 3.2g (user code, overhead)

Shuffle Configuration

python
# Shuffle partitions spark.conf.set("spark.sql.shuffle.partitions", "200") # Rule of thumb: 2-4 partitions per core # 40 cores × 3 = 120 partitions # Shuffle memory buffer spark.conf.set("spark.shuffle.file.buffer", "64k") spark.conf.set("spark.reducer.maxSizeInFlight", "96m") # Shuffle compression spark.conf.set("spark.shuffle.compress", "true") spark.conf.set("spark.shuffle.spill.compress", "true") spark.conf.set("spark.io.compression.codec", "snappy")
Shuffle ParameterDefaultDescription
spark.sql.shuffle.partitions200Partitions after shuffle
spark.shuffle.file.buffer32kBuffer size for shuffle writes
spark.reducer.maxSizeInFlight48mMax size of map output per reducer
spark.shuffle.compresstrueCompress shuffle output
spark.shuffle.spill.compresstrueCompress spilled data

Dynamic Allocation

Dynamic allocation adjusts executor count based on workload.

python
# Enable dynamic allocation spark.conf.set("spark.dynamicAllocation.enabled", "true") spark.conf.set("spark.dynamicAllocation.minExecutors", "2") spark.conf.set("spark.dynamicAllocation.maxExecutors", "50") spark.conf.set("spark.dynamicAllocation.initialExecutors", "5") spark.conf.set("spark.dynamicAllocation.executorIdleTimeout", "60s") spark.conf.set("spark.dynamicAllocation.cachedExecutorIdleTimeout", "120s") # With shuffle tracking (keeps executors during shuffles) spark.conf.set("spark.dynamicAllocation.shuffleTracking.enabled", "true")
Success

Dynamic allocation is essential for multi-tenant clusters. It automatically scales down when demand is low and scales up during peak loads, improving cluster utilization.

When to Use Dynamic Allocation

ScenarioRecommended
Multi-tenant YARN clusterYes
Dedicated clusterMaybe (fixed allocation simpler)
Streaming workloadsMaybe (consider fixed for predictable latency)
KubernetesYes (auto-scaling)
Short ad-hoc queriesYes

Adaptive Query Execution (AQE)

python
# Enable AQE (default in Spark 3.x) spark.conf.set("spark.sql.adaptive.enabled", "true") # Dynamic partition coalescing spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true") spark.conf.set("spark.sql.adaptive.coalescePartitions.minPartitions", "10") spark.conf.set("spark.sql.adaptive.coalescePartitions.maxPartitions", "500") spark.conf.set("spark.sql.adaptive.coalescePartitions.parallelismFirst", "true") # Skew join optimization spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true") spark.conf.set("spark.sql.adaptive.skewJoin.skewedPartitionFactor", "5") spark.conf.set("spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes", "256MB") # Dynamic join switch (broadcast vs sort-merge) spark.conf.set("spark.sql.adaptive.localShuffleReader.enabled", "true")

Serialization and Compression

python
# Kryo serialization (faster than Java serialization) spark.conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") spark.conf.set("spark.kryoserializer.buffer.max", "256m") spark.conf.set("spark.kryo.registrationRequired", "false") # Register custom classes for Kryo spark.conf.set("spark.kryo.classesToRegister", "com.example.MyClass,com.example.AnotherClass") # RDD compression spark.conf.set("spark.rdd.compress", "true") # Broadcast compression spark.conf.set("spark.broadcast.compress", "true")

Network and I/O

python
# Network timeouts spark.conf.set("spark.network.timeout", "600s") spark.conf.set("spark.executor.heartbeatInterval", "30s") spark.conf.set("spark.sql.broadcastTimeout", "600") # Max result size spark.conf.set("spark.driver.maxResultSize", "4g") # I/O optimizations spark.conf.set("spark.hadoop.parquet.enable.summary-metadata", "false") spark.conf.set("spark.sql.parquet.mergeSchema", "false") spark.conf.set("spark.sql.parquet.filterPushdown", "true")

Task and Parallelism

python
# Task configuration spark.conf.set("spark.task.maxFailures", "8") spark.conf.set("spark.speculation", "true") spark.conf.set("spark.speculation.interval", "100ms") spark.conf.set("spark.speculation.multiplier", "1.5") # Default parallelism # spark.default.parallelism = max(2, total_cores) # spark.sql.shuffle.partitions = 200

Common Configurations by Use Case

Batch ETL

python
spark.conf.set("spark.executor.memory", "4g") spark.conf.set("spark.executor.cores", "4") spark.conf.set("spark.sql.shuffle.partitions", "200") spark.conf.set("spark.sql.adaptive.enabled", "true") spark.conf.set("spark.dynamicAllocation.enabled", "true")

Machine Learning

python
spark.conf.set("spark.executor.memory", "16g") # More memory for ML spark.conf.set("spark.executor.cores", "4") spark.conf.set("spark.sql.shuffle.partitions", "100") spark.conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") spark.conf.set("spark.kryoserializer.buffer.max", "256m")

Streaming

python
spark.conf.set("spark.executor.memory", "8g") spark.conf.set("spark.executor.cores", "3") spark.conf.set("spark.sql.shuffle.partitions", "10") # Fewer partitions spark.conf.set("spark.streaming.backpressure.enabled", "true") spark.conf.set("spark.streaming.kafka.maxRatePerPartition", "1000")

Interactive/Ad-hoc

python
spark.conf.set("spark.executor.memory", "8g") spark.conf.set("spark.sql.shuffle.partitions", "50") spark.conf.set("spark.sql.adaptive.enabled", "true") spark.conf.set("spark.dynamicAllocation.enabled", "true") spark.conf.set("spark.dynamicAllocation.maxExecutors", "20")

Configuration CLI Examples

bash
# spark-submit with configuration spark-submit \ --class com.example.Main \ --master yarn \ --deploy-mode cluster \ --num-executors 20 \ --executor-memory 8g \ --executor-cores 4 \ --driver-memory 4g \ --conf spark.sql.shuffle.partitions=200 \ --conf spark.sql.adaptive.enabled=true \ --conf spark.dynamicAllocation.enabled=true \ --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \ app.jar

Practice Questions

  1. What is the formula for calculating total cluster resources?
  2. How does spark.memory.fraction divide executor memory?
  3. When should you enable dynamic allocation?
  4. What is the recommended value for spark.sql.shuffle.partitions?
  5. How does AQE improve shuffle partition distribution?
  6. What is the advantage of Kryo serialization over Java serialization?
  7. How does speculation help with straggler tasks?
  8. What configuration changes would you make for a streaming workload vs batch?
  9. Why is spark.sql.shuffle.partitions set lower for streaming?
  10. How do you configure Spark for a memory-intensive ML workload?
Progress60%