In PySpark, repartition performs a full shuffle to produce any partition count you ask for, evenly sized or grouped by a column. coalesce merges partitions the DataFrame already has, with no shuffle, so it is cheaper but can only lower the count and cannot even out uneven partitions. Coalesce to reduce; repartition to increase or rebalance.
Last updated October 2026.
The partition count is the parallelism of the next stage: one task per partition, one core per task. repartition(n) pays for a shuffle and can go up or down with even partitions. coalesce(n) skips the shuffle by gluing neighbors together: the cheap way down, and no way up.
This sits in Troubleshooting and Tuning, 10% of the Databricks Certified Associate Developer for Apache Spark exam per the official 30 Oct 2025 exam guide. Questions describe a symptom (idle cores, tiny files) and ask which call fixes it.
df.repartition(64) # exactly 64, rows dealt out evenly
df.repartition(64, "country") # 64, hashed on country
df.repartition("country") # hashed; count from spark.sql.shuffle.partitions
With a number only, every row is redistributed so the new partitions come out roughly equal. With columns, rows are hash partitioned so every row with the same key lands in the same partition; with columns and no number, the count is spark.sql.shuffle.partitions, 200 by default. Either way it is a wide transformation: every row is written to shuffle files and read back over the network, and a new stage starts.
df.coalesce(100) # 1000 partitions in, 100 out, no shuffle
Each of the 100 new partitions claims about 10 of the existing ones. That is a narrow dependency, so nothing crosses the network. Two rules follow from it:
df.coalesce(128) on an 8 partition DataFrame leaves it at 8, silently. No error, no change.Because coalesce adds no stage boundary, the work before it runs in the same stage, in the reduced number of tasks. A heavy parse or join followed by coalesce(1) can end up running entirely on one core. The PySpark API reference says this directly: a drastic coalesce may run your computation on fewer nodes than you like, and calling repartition() instead adds a shuffle step but keeps the upstream work parallel.
So when one output file is genuinely required after heavy work, repartition(1) is the safer call.
write.partitionBy("country") lays the output out as one folder per value. If rows for every country are scattered across every partition, every task writes a file into every folder, and the small files multiply. Grouping the rows first means each task writes into one folder:
(df.repartition("country")
.write.mode("overwrite")
.partitionBy("country")
.parquet("/data/output"))
Coalesce cannot do this, because it takes a number only and never regroups rows by key.
| repartition | coalesce | |
|---|---|---|
| Shuffle | Yes, every row | No: narrow dependency |
| Direction | Up or down | Down only; a larger number is ignored |
| Resulting sizes | Even, or grouped by key | Whatever the merged neighbors held |
| By column | Yes: repartition("col") | No, a number only |
| Upstream work | Keeps its own parallelism (new stage) | Shrinks to n tasks (same stage) |
| In explain() | Exchange RoundRobinPartitioning or hashpartitioning | Coalesce node, no Exchange |
Sourced from the PySpark API reference for DataFrame.repartition and DataFrame.coalesce and the Apache Spark SQL performance tuning guide.
coalesce(n), as long as the upstream work is light.repartition(n). Coalesce cannot raise the count.repartition(n), since only a shuffle evens them out.repartition("key"), typically before partitionBy("key").repartition(1), not coalesce(1).To confirm which one ran, read explain(): an Exchange is a shuffle, a Coalesce node is not. In SQL, the /*+ REPARTITION(c) */ and /*+ COALESCE(n) */ hints do the same.
Exam tip: "without a shuffle", "cheaply reduce", "fewer output files" points at coalesce. "Idle cores", "uneven partitions", "increase parallelism", "group by key before writing" points at repartition. If an option coalesces to a number higher than the current count, it does nothing.
The common distractor for raising parallelism is changing spark.sql.shuffle.partitions. That setting only applies to a shuffle's output, so it does nothing for a stage that reads files and never shuffles. Where partition counts come from (128 MB input splits, 200 shuffle partitions) is in Chapter 07, partitions and shuffles; join strategies are in the companion post on broadcast hash joins versus sort-merge joins.
To check where you stand across all seven sections, take the readiness quiz or read the section weights post.
repartition performs a full shuffle and can raise or lower the partition count, with even partitions or rows grouped by a column. coalesce merges existing partitions without a shuffle, so it is cheaper but can only lower the count.
No. On a DataFrame, coalesce with a number larger than the current partition count leaves the count unchanged, with no error. Use repartition to increase the partition count.
coalesce adds no stage boundary, so the work before it also runs in one task on one core. repartition(1) adds a shuffle but keeps the upstream work parallel, which is why the PySpark docs suggest it for a drastic reduction.
Often, yes. repartition on the same column groups each key into one partition, so each task writes into one folder instead of a small file in every folder. coalesce cannot do this because it never regroups rows by key.
repartition versus coalesce is a trade-off chapter in Unit 4 of the Spark Developer Associate course, with the plans, the decision signals and exam-style questions built around them.
Open the chapter →