Skip to main content
โšก Calmops

Apache Spark: Big Data Processing at Scale 2026

Introduction

Apache Spark has become the de facto standard for big data processing, offering unified analytics engine for large-scale data processing. Whether you’re processing terabytes of log data, training machine learning models, or running ad-hoc SQL queries, Spark provides the scalability and speed needed for modern data workloads.

In this comprehensive guide, we’ll explore Spark’s architecture, core concepts, programming APIs, and optimization techniques.


What is Apache Spark?

Apache Spark is a distributed computing system designed for fast, general-purpose cluster computing. It provides:

  • Speed: In-memory caching and optimized query execution
  • Ease of Use: APIs in Python, Scala, Java, and R
  • Unified Engine: Batch processing, streaming, ML, and graph analytics
  • Scalability: From a single laptop to thousands of nodes
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      SPARK ECOSYSTEM                                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                     SPARK CORE                               โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚  Spark SQL   โ”‚  โ”‚   Spark      โ”‚  โ”‚   GraphX    โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚              โ”‚  โ”‚   Streaming  โ”‚  โ”‚              โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚              MLlib (Machine Learning)                โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                              โ”‚                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                    DEPLOYMENT MODES                          โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚  Local   โ”‚  โ”‚ Standaloneโ”‚  โ”‚   YARN   โ”‚  โ”‚  Mesos   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚  Mode   โ”‚  โ”‚  Cluster  โ”‚  โ”‚ Cluster  โ”‚  โ”‚ Cluster  โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚            Kubernetes (Spark 3.0+)                    โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                              โ”‚                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                    STORAGE LAYERS                            โ”‚   โ”‚
โ”‚  โ”‚  HDFS  โ”‚  S3  โ”‚  Cassandra  โ”‚  Kafka  โ”‚  JDBC/ODBC      โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Spark Architecture

Core Concepts

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    SPARK ARCHITECTURE                                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                    DRIVER PROGRAM                           โ”‚   โ”‚
โ”‚  โ”‚  โ€ข Creates SparkContext                                     โ”‚   โ”‚
โ”‚  โ”‚  โ€ข Converts user code to Tasks                             โ”‚   โ”‚
โ”‚  โ”‚  โ€ข Schedules Tasks across Executors                        โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                              โ”‚                                       โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚              โ–ผ               โ–ผ               โ–ผ                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚  Executor 1     โ”‚ โ”‚  Executor 2     โ”‚ โ”‚  Executor N     โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚ โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚ โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Task 1   โ”‚  โ”‚ โ”‚  โ”‚ Task 2   โ”‚  โ”‚ โ”‚  โ”‚ Task 3   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Task 4   โ”‚  โ”‚ โ”‚  โ”‚ Task 5   โ”‚  โ”‚ โ”‚  โ”‚ Task 6   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚ โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚ โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚ โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚ โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Memory   โ”‚  โ”‚ โ”‚  โ”‚ Memory   โ”‚  โ”‚ โ”‚  โ”‚ Memory   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ (RDD)    โ”‚  โ”‚ โ”‚  โ”‚ (RDD)    โ”‚  โ”‚ โ”‚  โ”‚ (RDD)    โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚ โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚ โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                              โ”‚                                       โ”‚
โ”‚                              โ–ผ                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                    CLUSTER MANAGER                          โ”‚   โ”‚
โ”‚  โ”‚           (YARN, Mesos, Kubernetes, Standalone)             โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key Components

  • Driver: Creates SparkContext, converts code to DAG, schedules tasks
  • Executor: Runs tasks, stores data in memory/disk
  • Cluster Manager: Allocates resources (YARN, Mesos, K8s)
  • DAG Scheduler: Breaks job into stages and tasks
  • Task Scheduler: Sends tasks to executors

Programming APIs

RDD (Resilient Distributed Dataset)

RDDs are the fundamental data structure in Spark - immutable, distributed collections that can be processed in parallel.

from pyspark import SparkContext

# Initialize Spark Context
sc = SparkContext(appName="MyApp")

# Create RDD from data
data = [1, 2, 3, 4, 5]
rdd = sc.parallelize(data)

# Basic transformations
squared_rdd = rdd.map(lambda x: x ** 2)
filtered_rdd = rdd.filter(lambda x: x > 2)

# Actions
result = rdd.collect()  # [1, 2, 3, 4, 5]
sum_result = rdd.reduce(lambda a, b: a + b)  # 15
count = rdd.count()  # 5

# Create RDD from file
text_rdd = sc.textFile("hdfs://path/to/file.txt")
word_counts = text_rdd \
    .flatMap(lambda line: line.split()) \
    .map(lambda word: (word, 1)) \
    .reduceByKey(lambda a, b: a + b)

DataFrame API

DataFrames provide a higher-level abstraction with named columns, similar to pandas DataFrames or SQL tables.

from pyspark.sql import SparkSession

# Create SparkSession
spark = SparkSession.builder \
    .appName("DataFrameExample") \
    .config("spark.master", "local[*]") \
    .getOrCreate()

# Create DataFrame from Python list
data = [
    ("Alice", 25, "NYC"),
    ("Bob", 30, "LA"),
    ("Charlie", 35, "NYC"),
    ("Diana", 28, "SF")
]

df = spark.createDataFrame(data, ["name", "age", "city"])

# Or create from CSV
df = spark.read.csv("data.csv", header=True, inferSchema=True)

# Or create from Parquet
df = spark.read.parquet("data.parquet")

# Show schema
df.printSchema()
# root
#  |-- name: string (nullable = true)
#  |-- age: integer (nullable = true)
#  |-- city: string (nullable = true)

# Basic operations
df.show()
df.columns
df.dtypes
df.count()

# Select columns
df.select("name", "age").show()
df.select(df.name, df.age + 1).withColumnRenamed("age + 1", "age_next").show()

# Filter
df.filter(df.age > 30).show()
df.filter((df.city == "NYC") & (df.age > 20)).show()

# Aggregations
from pyspark.sql import functions as F

df.groupBy("city").count().show()
df.groupBy("city").agg(
    F.count("*").alias("count"),
    F.avg("age").alias("avg_age"),
    F.max("age").alias("max_age")
).show()

# SQL queries
df.createOrReplaceTempView("people")
spark.sql("SELECT city, COUNT(*) as count FROM people GROUP BY city").show()

# Window functions
from pyspark.sql.window import Window

window_spec = Window.partitionBy("city").orderBy("age")
df.withColumn("rank", F.row_number().over(window_spec)).show()

Spark SQL

Spark SQL allows you to run SQL queries on your data with full optimization capabilities.

# Register DataFrame as table
df.createOrReplaceTempView("employees")

# Run SQL queries
result = spark.sql("""
    SELECT 
        city,
        COUNT(*) as employee_count,
        AVG(age) as avg_age,
        MAX(age) as max_age,
        MIN(age) as min_age
    FROM employees
    WHERE age > 20
    GROUP BY city
    HAVING COUNT(*) > 1
    ORDER BY employee_count DESC
""")

result.show()

# Complex queries
spark.sql("""
    SELECT 
        city,
        COUNT(*) as total,
        SUM(CASE WHEN age >= 30 THEN 1 ELSE 0 END) as senior_count,
        AVG(age) as avg_age
    FROM employees
    WHERE city IN ('NYC', 'LA', 'SF')
    GROUP BY city
""").show()

# Join operations
sales_df = spark.read.parquet("sales.parquet")
employees_df = spark.read.parquet("employees.parquet")

# Join DataFrames
result = employees_df.join(
    sales_df,
    employees_df.id == sales_df.employee_id,
    "inner"
)

# Or use SQL
spark.sql("""
    SELECT e.name, s.amount
    FROM employees e
    INNER JOIN sales s ON e.id = s.employee_id
""").show()

Structured Streaming

Structured Streaming provides stream processing on top of Spark SQL engine.

from pyspark.sql import SparkSession
from pyspark.sql.functions import *

spark = SparkSession.builder \
    .appName("StreamingExample") \
    .getOrCreate()

# Read streaming data from Kafka
kafka_df = spark \
    .readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "localhost:9092") \
    .option("subscribe", "transactions") \
    .load()

# Parse JSON
parsed_df = kafka_df.select(
    from_json(col("value").cast("string"), "schema").alias("data")
).select("data.*")

# Process streaming data
processed_df = parsed_df \
    .withWatermark("timestamp", "10 minutes") \
    .groupBy(
        window(col("timestamp"), "5 minutes"),
        col("merchant_id")
    ) \
    .agg(
        sum("amount").alias("total_amount"),
        count("*").alias("transaction_count"),
        avg("amount").alias("avg_amount")
    )

# Write to console (for debugging)
query = processed_df \
    .writeStream \
    .format("console") \
    .outputMode("complete") \
    .start()

# Write to Kafka
kafka_output = processed_df \
    .select(to_json(struct("*")).alias("value")) \
    .writeStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "localhost:9092") \
    .option("topic", "aggregated_transactions") \
    .option("checkpointLocation", "/tmp/checkpoint") \
    .start()

# Write to Delta Lake
delta_output = processed_df \
    .writeStream \
    .format("delta") \
    .option("checkpointLocation", "/tmp/delta_checkpoint") \
    .outputMode("complete") \
    .start("/delta/transactions")

# Wait for termination
query.awaitTermination()

Machine Learning with MLlib

Spark includes MLlib for distributed machine learning.

from pyspark.sql import SparkSession
from pyspark.ml import Pipeline
from pyspark.ml.feature import VectorAssembler, StringIndexer, StandardScaler
from pyspark.ml.classification import RandomForestClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator

spark = SparkSession.builder \
    .appName("MLExample") \
    .getOrCreate()

# Load training data
training_data = spark.read.parquet("training_data.parquet")

# Prepare features
feature_columns = ["amount", "age", "transaction_count", "balance"]
assembler = VectorAssembler(
    inputCols=feature_columns,
    outputCol="features"
)

# Scale features
scaler = StandardScaler(
    inputCol="features",
    outputCol="scaled_features",
    withMean=True,
    withStd=True
)

# Create pipeline
pipeline = Pipeline(stages=[
    assembler,
    scaler,
    RandomForestClassifier(
        labelCol="fraud",
        featuresCol="scaled_features",
        numTrees=100,
        maxDepth=10
    )
])

# Train model
model = pipeline.fit(training_data)

# Make predictions
predictions = model.transform(training_data)
predictions.select("fraud", "prediction", "probability").show()

# Evaluate
evaluator = BinaryClassificationEvaluator(
    labelCol="fraud",
    rawPredictionCol="prediction"
)
accuracy = evaluator.evaluate(predictions)
print(f"Accuracy: {accuracy}")

Optimization Techniques

Partitioning Strategies

# Repartition to optimize parallelism
df_repartitioned = df.repartition(100)

# Coalesce to reduce partitions (for writing)
df_repartitioned.coalesce(10).write.parquet("output.parquet")

# Partition by column for efficient queries
df.write.partitionBy("year", "month").parquet("output/")

# Bucketing for join optimization
df1.write.bucketBy(100, "id").sortBy("id").saveAsTable("table1")
df2.write.bucketBy(100, "id").sortBy("id").saveAsTable("table2")

Caching and Persistence

# Cache frequently accessed DataFrame
df_cached = df.filter(...).select(...)
df_cached.cache()  # Store in memory
df_cached.count()  # Materialize cache

# Or use persist for custom storage level
from pyspark.storagelevel import StorageLevel

df_cached.persist(StorageLevel.MEMORY_AND_DISK)

# Unpersist when done
df_cached.unpersist()

Query Optimization

# Use Spark SQL hints
spark.sql("""
    SELECT /*+ BROADCAST(small_table) */ *
    FROM large_table
    JOIN small_table ON large_table.id = small_table.id
""")

# Enable Adaptive Query Execution (Spark 3.0+)
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true")

# Broadcast small tables
from pyspark.sql.functions import broadcast

result = large_df.join(broadcast(small_df), "key")

# Avoid UDFs when possible - use built-in functions
from pyspark.sql.functions import col, upper, lower, trim

# Bad: Python UDF
def classify(age):
    if age < 18:
        return "minor"
    elif age < 65:
        return "adult"
    return "senior"

udf_classify = udf(classify)
df.select(udf_classify("age"))  # Slow!

# Good: Built-in functions
from pyspark.sql.functions import when, col
df.select(
    when(col("age") < 18, "minor")
    .when(col("age") < 65, "adult")
    .otherwise("senior")
    .alias("age_class")
)

Memory Management

# Configure memory settings
spark = SparkSession.builder \
    .config("spark.driver.memory", "4g") \
    .config("spark.executor.memory", "8g") \
    .config("spark.memory.fraction", "0.6") \
    .config("spark.memory.storageFraction", "0.5") \
    .getOrCreate()

# For large shuffles
spark.conf.set("spark.sql.shuffle.partitions", 200)
spark.conf.set("spark.default.parallelism", 100)

Common Pitfalls

1. Not Using Broadcast Joins for Small Tables

# Anti-pattern: Shuffle join for small table
def bad_join():
    result = large_df.join(small_df, "key")  # Shuffles both!
    return result

# Good pattern: Broadcast small table
def good_join():
    from pyspark.sql.functions import broadcast
    result = large_df.join(broadcast(small_df), "key")
    return result

2. Collecting Large Data to Driver

# Anti-pattern: collect() on large DataFrame
def bad_collect():
    # This will crash with OOM!
    data = large_df.collect()  # Moves ALL data to driver
    return data

# Good pattern: Use limit or sample
def good_collect():
    sample = large_df.limit(1000).collect()  # Only small amount
    return sample

# Or use toPandas with limit
def pandas_with_limit():
    pdf = large_df.limit(10000).toPandas()  # Small amount to pandas
    return pdf

3. Not Using Checkpoints for Long Lineages

# Anti-pattern: Long lineage chain
def bad_lineage():
    # After many transformations, lineage becomes too long
    df = spark.read.parquet("data")
    for i in range(100):
        df = df.filter(col("value") > i)
        df = df.withColumn("value", col("value") + 1)
    # Checkpoint to truncate lineage
    df = df.checkpoint()  # Truncates lineage!
    return df

Best Practices

1. Use DataFrames Over RDDs

# DataFrames have:
# - Catalyst optimizer
# - Tungsten execution engine
# - Better memory management
# - SQL support

# Use DataFrames for most workloads
df = spark.read.parquet("data.parquet")
result = df.filter(col("value") > 100).groupBy("category").count()

2. Handle Skew in Joins

def handle_skew():
    # Skew join optimization
    spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")
    
    # Or manually handle
    # Add salt to skewed key
    from pyspark.sql.functions import lit, rand
    
    skewed_df_with_salt = skewed_df.withColumn(
        "salt", 
        (rand() * n_partitions).cast("int")
    )
    
    # Broadcast small table with salt
    small_df_salted = small_df.crossJoin(
        spark.range(n_partitions).withColumnRenamed("id", "salt")
    )
    
    # Join
    result = skewed_df_with_salt.join(
        small_df_salted,
        ["key", "salt"]
    ).drop("salt")

3. Use Proper File Formats

# Prefer Parquet for analytical workloads
# - Columnar format (good for scans)
# - Schema evolution support
# - Compression support
# - Predicate pushdown

# Write as Parquet
df.write.parquet("output/", mode="overwrite", compression="snappy")

# Read with predicate pushdown
df = spark.read.parquet(
    "output/",
    filters=[("year", "=", 2024), ("month", "=", 1)]
)

External Resources


Conclusion

Apache Spark is a powerful unified analytics engine that can handle diverse big data workloads. Understanding its architecture, programming APIs, and optimization techniques is essential for any data engineer.

Key takeaways:

  • Use DataFrames over RDDs for better optimization
  • Leverage Spark SQL for familiar SQL workflows
  • Use Structured Streaming for real-time processing
  • Apply broadcast joins for small tables
  • Configure proper partitioning and memory settings
  • Use Parquet for analytical workloads with predicate pushdown

With proper optimization, Spark can process petabytes of data efficiently across clusters of thousands of nodes.

Comments