RDD转换操作中的 关于数据分区coalesce 和 repartition 的区别
1. 机制详解
(1) coalesce()
-
工作原理:
通过合并 现有分区 来减少分区数,默认仅在 同一 Executor 内的分区之间合并,避免跨节点数据传输。-
示例:若原始分区分布在 Executor1 (P1, P2)、Executor2 (P3, P4),执行
coalesce(2)
可能合并为 Executor1 (P1+P2)、Executor2 (P3+P4)。
-
-
代码示例:
scala
val rdd = sc.parallelize(1 to 100, 10) // 初始 10 个分区 val coalesced = rdd.coalesce(2) // 合并为 2 个分区(无 Shuffle)
(2) repartition()
-
工作原理:
通过 全量 Shuffle 重新分配数据到新分区,确保数据均匀分布。-
底层实现:
repartition()
实际调用coalesce(numPartitions, shuffle = true)
。 -
示例:无论原始分区分布,执行
repartition(4)
会触发 Shuffle,重新均匀分配数据到 4 个新分区。
-
-
代码示例:
scala
val rdd = sc.parallelize(1 to 100, 5) // 初始 5 个分区 val repartitioned = rdd.repartition(8) // 增加至 8 个分区(触发 Shuffle)
2. 使用场景对比
何时用 coalesce()
?
-
减少分区数:过滤后数据量大幅减少时,避免空分区或过度并行化。
-
避免 Shuffle 开销:对数据分布均匀性不敏感时(如写入文件前减少输出文件数)。
-
优化资源利用:合并空闲分区,减少任务调度开销。
何时用 repartition()
?
-
增加分区数:提升并行度(如从上游 Shuffle 的少量分区恢复到合理分区数)。
-
平衡数据分布:解决数据倾斜(如某些分区数据量远大于其他分区)。
-
强制数据洗牌:确保后续操作的数据局部性(如 JOIN 前重新分区)。
3. 性能影响对比
操作 | coalesce() | repartition() |
---|---|---|
网络开销 | 低(局部合并) | 高(全量 Shuffle) |
数据移动量 | 少(仅合并相邻分区) | 多(全局重新分配) |
执行速度 | 快 | 慢(依赖 Shuffle 速度) |
4. 误区与注意事项
-
coalesce()
无法增加分区:
若coalesce(n)
中n
大于当前分区数,操作无效(分区数不变)。scala
val rdd = sc.parallelize(1 to 100, 5) rdd.coalesce(10).getNumPartitions // 仍为 5
-
数据倾斜风险:
coalesce()
合并分区可能导致某些分区数据量过大,需谨慎使用。 -
强制触发 Shuffle:
coalesce(shuffle = true)
等效于repartition()
,但代码可读性较差。
5.举个栗子 🌰
假设你有 10 箱苹果(相当于 10 个分区),现在想重新整理:
-
coalesce
的做法:-
把相邻的箱子直接合并(比如 10 箱 → 5 箱)。
-
优点:省时间,不用倒出来重新装。
-
缺点:可能有的箱子苹果多,有的少(数据倾斜)。
-
适用场景:苹果变少了(比如扔掉了烂苹果),箱子太多没必要。
-
-
repartition
的做法:-
把苹果全部倒出来,重新均匀分装到新箱子(比如 10 箱 → 15 箱 或 10 箱 → 5 箱)。
-
优点:每个箱子苹果数量差不多(数据平衡)。
-
缺点:费时间,要重新整理。
-
适用场景:苹果很多且要分给更多人处理(增加并行度),或者原来箱子大小不均。
-
一句话总结
-
coalesce
:偷偷合并箱子(不重新整理),适合减少分区。 -
repartition
:彻底重新分装(全部倒出来),适合增加分区或解决数据倾斜。
小白选哪个?
场景 | 选谁 | 代码示例 |
---|---|---|
数据量减少,想合并分区省资源 | coalesce | df.coalesce(2) |
数据倾斜,要均匀分布 | repartition | df.repartition(10) |
不确定,但想简单试试 | repartition | df.repartition(5) (无脑用这个也行) |
附赠口诀
“减省用coal,加匀用re”
(减少分区省资源用coalesce
,增加分区或均匀数据用repartition
)