A unified analytics engine designed for large-scale data processing. It provides an interface for programming clusters with data parallelism and fault tolerance.
collect or save) is triggered.Basic data structure with operations like map, filter, and reduce. Provides fault tolerance through lineage.
High-level data structure optimized for querying, backed by Catalyst optimizer. Supports SQL queries, making it a preferred choice for structured data.
Type-safe, object-oriented API available in languages like Scala. Offers benefits of both RDDs and DataFrames but is limited in Python.
Examples: map, filter, flatMap, union, groupByKey. These are lazy operations that produce new RDDs but don’t execute until an action is called.
Examples: collect, count, reduce, saveAsTextFile. Actions trigger the execution of transformations and return results to the driver.
Catalyst Optimizer (for SQL and DataFrames): Uses rule-based and cost-based optimization to produce efficient query plans.
Memory Management: Manages caching, shuffle files, and resource allocation for performance.
Nothing runs until an action such as count() or write is called. At that point the Catalyst optimizer turns the DataFrame operations into an optimised physical plan, and the DAG scheduler cuts it into stages wherever data must be redistributed across the cluster. Each stage runs one task per partition on the executors. Adaptive Query Execution (enabled by default in Spark 3.x) re-plans at stage boundaries using runtime statistics, for example coalescing small shuffle partitions or switching to a broadcast join. On Databricks, the Photon engine can execute many of these operators natively for additional speed.
| API | Use when | Avoid when |
|---|---|---|
| DataFrame / Spark SQL | Almost always: optimised by Catalyst, works in Python, SQL, Scala | You need low-level control of partition contents |
| RDD | Custom partition logic, unstructured records, legacy code | Structured data; you lose Catalyst optimisation |
| Dataset (Scala/Java) | Compile-time type safety on the JVM | Python (not available) |
| Structured Streaming | Incremental processing of unbounded data with the DataFrame API | One-off batch jobs |
| pandas API on Spark | Porting pandas code to larger data | Performance-critical code that needs tuned Spark idioms |
from pyspark.sql import SparkSession, functions as F
spark = SparkSession.builder.appName("demo").getOrCreate()
orders = spark.range(1_000_000).withColumn("cust", F.col("id") % 1000)
custs = spark.range(1000).withColumnRenamed("id", "cust").withColumn("tier", F.col("cust") % 3)
agg = (orders.join(F.broadcast(custs), "cust") # avoid shuffling the large side
.groupBy("tier").count()) # wide: causes a shuffle
agg.explain(mode="formatted") # look for Exchange and BroadcastHashJoin
print(agg.collect())
In the plan, each Exchange node is a shuffle and therefore a stage boundary. The broadcast hint ships the small table to every executor so the large table is joined without moving it.
collect() on large results, which overloads the driver.Related: PySpark, Databricks performance, Delta Lake.