Partitions as the unit of parallelism: repartition vs coalesce, partition sizing, and partition pruning.
⏱ 10 min readTopics chapter readerLevel · Performance
01 · Orientation
Start here
⏱ 6 min · Topic 1 of 9
In the chapter "Spark Architecture: Driver, Executors & Cluster Managers", one partition became one task. So the number of partitions decides how much work can run at the same time. This chapter shows how to see that number and change it.
The word partition has two meanings in Spark work. Keep them apart.
Question
Spark partition
Folder partition
Where is it?
In memory, while a job runs
On disk, in a folder name
What does it set?
How many tasks run
What a reader can skip
How do you set it?
repartition, coalesce
write.partitionBy
The chapter "Storage Layers & File Formats" taught the second meaning: date folders and small files. This chapter teaches the first meaning, then joins the two.
This chapter has four parts.
How partitions and cores work together, and how to see your partitions.
Where the partition count comes from, and what count to aim for.
How repartition and coalesce change the count.
How partitions decide the files and folders you write.
We use the shop again: 6 orders, and a cluster with 2 executors and 2 cores each. That gives 4 slots. A slot runs one task at a time.
02 · Partitions and cores
One partition, one task
⏱ 9 min · Topic 2 of 9
Spark cuts a table into pieces. Each piece is a partition. Spark runs one task for each partition.
In our model, orders has 3 partitions. Look at which order is in which partition.
orders in 3 partitions (our model)
Partition
Orders inside
0
101, 102
1
103, 104
2
105, 106
That means 3 tasks. The cluster has 4 slots.
A wave is one round of tasks that run at the same time. Here is the single wave.
3 partitions on 4 slots
Wave
Slot 1
Slot 2
Slot 3
Slot 4
Wave 1
Task 0
Task 1
Task 2
idle
Slot 4 has nothing to do. This is the first problem: too few partitions leave cores idle.
With 1 partition, 3 of the 4 slots would be idle. More cores would not help.
Now give the same data 8 partitions. There are 8 tasks and 4 slots, so Spark needs two waves.
8 partitions on 4 slots
Wave
Slot 1
Slot 2
Slot 3
Slot 4
Wave 1
Task 0
Task 1
Task 2
Task 3
Wave 2
Task 4
Task 5
Task 6
Task 7
Every slot is busy now. In a real run, a free slot takes the next task at once. So the waves are a simple picture.
But 8 partitions for 6 orders is too many. At most 6 partitions can hold a row, so at least 2 tasks have nothing to read. Every task also costs time to send to an executor and to start.
03 · Look inside
See your partitions
⏱ 5 min · Topic 3 of 9
You can ask Spark how many partitions a DataFrame has. Use getNumPartitions on its rdd.
Count the partitionsworked example
SQL
Input data
orders in 3 partitions (our model)3 rows
partition
order_ids
0
101, 102
1
103, 104
2
105, 106
print(orders.rdd.getNumPartitions())
print shows
output
3
The model has 3 partitions, so Spark runs 3 tasks for a scan of orders.
To see which row is in which partition, add the function spark_partition_id(). It returns the partition number for each row. Numbers start at 0.
All three partitions hold 2 rows, so the work is even. A partition with far more rows than the others would make one slow task.
04 · How many
Where the count comes from
⏱ 9 min · Topic 4 of 9
The partition count is not random. It comes from two places: reading files, and shuffles. We start with reading files.
When Spark reads files, it cuts big files into pieces. Each piece is at most spark.sql.files.maxPartitionBytes. The default is 128 MB.
Spark also packs many small files into one partition. The table shows three cases.
Partitions from a file read (default 128 MB)
What you read
Partitions
One file of 1,024 MB (1 GB)
8 (1,024 / 128)
Many files of 1 MB each
Several files packed into each partition
Spark also tries to give every core work. So on a cluster with many cores, a small file can give more than 1 partition.
Spark can cut formats like Parquet. It cannot cut a gzip text file, such as orders.csv.gz. So that file becomes one partition, however big it is.
The second place is a shuffle. The chapter "The Shuffle: Spark's Performance Killer" showed the setting spark.sql.shuffle.partitions. Its default is 200.
The groupBy shuffles, and its new stage now has 8 tasks instead of 200. The shop has 4 customers, so at most 4 of those partitions hold a row. With AQE (adaptive query execution) on, Spark can also merge small partitions for you.
How many partitions should you aim for? A common starting point: first aim for about 100 to 200 MB in each partition. Then check that you have at least 2 to 4 partitions for each core.
Count at about 128 MB per partition
Data size
Partitions
1 GB
8
50 GB
400 (51,200 MB / 128)
These are starting points, not laws. A partition that is too big can fill memory, and then Spark spills it. Spilling means Spark writes data to disk because memory is full.
Check the Spark UI after a run. Look at the task times and the spill numbers. Then change the count and try again.
05 · Change the count
repartition or coalesce
⏱ 10 min · Topic 5 of 9
Spark has two tools to change the partition count. We start from our 3 partitions: 101, 102 | 103, 104 | 105, 106.
coalesce(n) merges existing partitions. It does not shuffle, so no row moves across the network.
Partition 0 stays as it was. Partitions 1 and 2 merge into the new partition 1. The sizes are 2 and 4: coalesce can leave uneven partitions. Which ones Spark merges is a model here.
repartition(n) is different. It does a full shuffle and sends every row to a new partition.
It aims for equal sizes. It can raise or lower the count.
repartition(2): full shuffle, even sizesworked example
Both partitions hold 3 rows. The sizes are the point. Exactly which order goes where can change from run to run.
You can also give a column: repartition(n, "customer_id"). Spark sends rows with the same customer_id to the same partition. It does this with a hash of the column.
repartition(2, "customer_id"): same key, same partitionworked example
Customer 3 has orders 104 and 105, and both are in partition 1. Real Spark uses a hash, so real placement differs. The rule is the same. The sizes are 2 and 4, so a key can make partitions uneven.
This table puts the three calls side by side.
The three calls side by side
Call
Shuffle?
Sizes
Can raise the count?
coalesce(n)
No
Can be uneven
No
repartition(n)
Yes, all rows
Even
Yes
repartition(n, col)
Yes, all rows
Depends on the keys
Yes
If you write repartition("customer_id") without a number, Spark uses spark.sql.shuffle.partitions. That default is 200.
Watch out
With repartition(col), one very common key makes one very big partition. Then one task runs long while the others wait. The chapter "Data Skew & Salting" shows how to fix it.
Interview question
"What is the difference between repartition and coalesce?" Give the three rows of the table above. Then say when you use each. Use coalesce just before a write, to make fewer files. Use repartition when you need more partitions or even sizes.
06 · Writing
Partitions and output files
⏱ 5 min · Topic 6 of 9
A write makes one file for each partition. Each task writes its own file. So the partition count sets the file count.
Write the 3 partitionsworked example
SQL
Input data
orders in 3 partitions (our model)3 rows
partition
order_ids
0
101, 102
1
103, 104
2
105, 106
orders.write.parquet("out/orders")
Files in out/orders (names shortened)
file
rows inside
part-00000
101, 102
part-00001
103, 104
part-00002
105, 106
Three partitions, three files. If you call coalesce(2) before the write, you get 2 files instead.
Too many partitions give too many small files. After a shuffle with the default 200 partitions, a small write can leave up to 200 tiny files. The chapter "Storage Layers & File Formats" showed why small files slow every reader.
The fix is to lower the count just before the write. coalesce is cheap, because it does not shuffle. Now the smallest case: one file.
One file with all 6 rows. For 6 rows this is fine. For a big table, one task must write everything, and the other slots wait.
Watch out
coalesce(1) does not shuffle. So the steps before it, like filters, also run in that 1 task. If you need one file from a big job, use repartition(1). Then the earlier steps still run as many tasks at the same time. It costs one shuffle.
07 · Folders
partitionBy and skipping folders
⏱ 8 min · Topic 7 of 9
Now the second meaning of partition: folders on disk. The call write.partitionBy("order_date") writes one folder for each date.
The 6 orders have 6 different dates, so there are 6 folders. The date is in the folder name, not in the file.
A reader can now skip folders. A filter on order_date opens only the matching folder. You met this in the chapter "Storage Layers & File Formats": it is called partition pruning.
Spark opens only the folder order_date=2026-01-10. The other 5 folders are never read.
Both meanings meet at the write. Each task writes its own file into every folder it has rows for. So the Spark partitions decide how many files each folder gets.
Imagine a real shop. Each day has orders in all 3 Spark partitions. Then every date folder gets 3 small files.
Files in one date folder (imagined busy day)
Before the write
Files in the folder
No repartition: rows of that day are in 3 partitions
3
repartition("order_date"): all rows of that day in 1 partition
1
So repartition by the same column first. Then each date lands in one partition, and each folder gets one file.
One very busy date then becomes one big task and one big file.
The shuffle puts all rows of one date together. Each date folder then holds one file instead of many small ones.
Watch out
Pick a column with a small number of values, such as a date. A column like customer_id or a timestamp makes thousands of folders. Each folder holds tiny files, and you get the small-file problem again.
08 · Recap
Check yourself
⏱ 4 min · Topic 8 of 9
Four points to keep.
One Spark partition is one task. Compare the partition count with the number of slots.
The count comes from file sizes when you read, and from spark.sql.shuffle.partitions after a shuffle.
coalesce merges without a shuffle and can be uneven. repartition shuffles and can make even sizes.
One file is written for each partition, in each folder. Repartition by the folder column before partitionBy.
Answer these without looking back.
You have 3 partitions and 4 slots. How many slots are idle, and what would you change?
A 1 GB file is read with the default 128 MB. How many partitions do you get?
Why can coalesce(2) give sizes 2 and 4, while repartition(2) gives 3 and 3?
You must write one file from a big job. Why is coalesce(1) risky?
09 · Next Chapter
Next Chapter
You can now count and see partitions, change the count, and write the files and folders you meant.
⏱ 3 min · Topic 9 of 9
Next chapter
The Shuffle: Spark's Performance Killer
You can now count and see partitions, change the count, and write the files and folders you meant.
Next is the shuffle chapter: what starts a shuffle, how it moves rows between partitions, and how to make it smaller.