Apache Spark Architecture and Components · Topic 3 of 4

Execution Hierarchy, Partitions and Shuffles

Databricks Spark Developer: how jobs, stages and tasks relate, what a shuffle is, and how repartition, coalesce and shuffle partitions set the counts.

  • 5 min
  • Includes a diagram
In a nutshell
  • Each action starts a job. Wide transformations split a job into stages. A stage has one task per partition.
  • Narrow transformations stay in one stage. Wide ones need a shuffle.
  • spark.sql.shuffle.partitions defaults to 200 and spark.sql.files.maxPartitionBytes defaults to 128 MB.

What it is

Spark runs work in a fixed hierarchy: an application contains jobs, a job contains stages, and a stage contains tasks. Each level has one clear trigger, and exam questions ask you to count them.

A partition is a slice of the data that one task processes. A shuffle is the redistribution of data across partitions and machines, needed when rows with the same key must end up together.

Applicationone per actionJobsplit at each shuffleStageone per partitionTaskruns on one core

How it works

The hierarchy

Execution hierarchy: an application with one SparkSession starts a job, usually one per action such as count or write; the job splits into stages at each shuffle boundary, and each stage runs as tasks, one per partition, each on one core.

LevelCreated byCount rule
ApplicationOne SparkSession and its driverOne per spark-submit or notebook session
JobEach action such as count, collect or writeUsually one job per action
StageShuffle boundaries inside a jobNumber of shuffles plus one, in a simple plan
TaskOne unit of work on one partitionOne per partition per stage

Take df.filter(...).groupBy("k").count().collect(). The collect() is the action, so there is one job. The groupBy needs a shuffle, so the job has two stages: read, filter and partial count before the shuffle, then final count after it. If the input has 40 partitions and shuffle partitions are 200, stage one has 40 tasks and stage two has 200, unless adaptive execution coalesces the shuffle partitions.

Narrow and wide transformations

TypeNeeds a shuffleExamples
NarrowNo. Each output partition reads one input partitionselect, filter, withColumn, union, coalesce
WideYes. Output partitions read many input partitionsgroupBy, join (unless broadcast), distinct, orderBy, repartition

Narrow transformations are pipelined inside one stage. A wide transformation ends a stage, writes shuffle files and starts a new stage. This is why shuffles are costly: they use disk, network and a wait for every task in the earlier stage.

Configuring partitions

Setting or callEffect
spark.sql.files.maxPartitionBytesTarget size of an input partition when reading files. Default 128 MB
spark.sql.shuffle.partitionsNumber of partitions after a shuffle in DataFrame and SQL. Default 200
repartition(n)Full shuffle to exactly n roughly equal partitions. Can increase or decrease
repartition(n, "col")Full shuffle that hashes rows by column, so equal keys land together
coalesce(n)Merges partitions without a full shuffle. Can only decrease
spark.conf.set("spark.sql.shuffle.partitions", "64")
df = spark.read.parquet("/data/events")
by_user = df.repartition(32, "user_id")
small = by_user.coalesce(8)
print(small.rdd.getNumPartitions())

The last line prints 8. Use coalesce to reduce partitions cheaply, for example before writing fewer output files. Use repartition when you need more partitions, an even spread, or co-location by key.

Choosing a partition count

Too few partitions leave cores idle and risk memory pressure per task. Too many create scheduling overhead and tiny tasks. The Spark tuning guide recommends two to three tasks per CPU core in the cluster.

Adaptive Query Execution, on by default since Spark 3.2, can coalesce small shuffle partitions at run time, so the 200 default is often a starting point rather than the final count. Tuning AQE is covered in the troubleshooting chapter.

Seeing the hierarchy in the Spark UI

The Spark UI lists each job, the stages inside it and the number of tasks per stage, so you can check your count against reality. A stage with one very slow task among many fast ones points to data skew. A stage with thousands of tiny tasks points to too many partitions.

Shuffle data is written to the local disks of the executors and fetched by the next stage. That is why a lost executor during a shuffle can force earlier work to be recomputed.

How the exam asks about this

  • "A job calls count() once and collect() once on two different DataFrames. How many jobs?" Two, one per action.
  • "A groupBy followed by a join of two large tables." Each wide step adds a stage boundary, so there are several stages.
  • "Reduce 500 partitions to 10 before writing, avoiding a full shuffle." Use coalesce(10).

What to remember for the exam

  • Each action starts a job. Wide transformations split a job into stages. A stage has one task per partition.
  • Narrow transformations stay in one stage. Wide ones need a shuffle.
  • spark.sql.shuffle.partitions defaults to 200 and spark.sql.files.maxPartitionBytes defaults to 128 MB.
  • repartition always shuffles and can go up or down. coalesce avoids a full shuffle and only goes down.
  • Partitioning by a column with repartition(n, "col") puts equal keys in the same partition.
  • Tasks per stage is bounded by partitions, not by cores.
Watch out

coalesce cannot increase the partition count. df.coalesce(100) on a DataFrame with 20 partitions still has 20.

Common mistakes

Counting stages by the number of transformations. Only wide transformations create a stage boundary. Ten chained narrow steps are still one stage.

Using repartition just to shrink output. It works, but it pays for a full shuffle that coalesce avoids. Coalesce can leave uneven partitions, which is the trade off.

Expecting changes to shuffle partitions to affect narrow steps. The setting applies after a shuffle, not to the partitions created when reading files.

Check yourself

  • Why does repartition(8) cost more than coalesce(8) on a DataFrame with 100 partitions?

    Repartition performs a full shuffle of the data, while coalesce merges neighboring partitions without one.

  • How many stages does df.groupBy("k").count().show() have in a simple plan?

    Two, because the groupBy shuffle splits the job into a stage before it and a stage after it.

  • What decides how many tasks a stage runs?

    The number of partitions in that stage, with one task per partition.

Topic 3 of 4 in Apache Spark Architecture and Components

Test yourself on Apache Spark Architecture and Components

The full Databricks Associate Developer for Apache Spark question bank has 600 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Databricks. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.