基于本文回答
0
评论

Spark repartition 和 coalesce 的区别是什么?

知识点图片

在 Apache Spark 中,repartitioncoalesce 都是用来改变 RDD 或 DataFrame 的分区数量(Parallelism)的算子,但它们在实现机制性能开销适用场景上有本质的区别。

最核心的区别在于:是否进行 Shuffle(洗牌/数据混洗)。

以下是详细的对比分析:

1. 核心区别总结

特性 repartition(n) coalesce(n)
Shuffle(洗牌) 总是发生 (Full Shuffle) 默认不发生 (No Shuffle)
主要用途 增加分区数,或重新平衡数据 减少分区数
性能 较低(涉及网络传输和磁盘I/O) 较高(本地合并,最小化数据移动)
数据分布 数据会被均匀分布(解决数据倾斜) 数据分布取决于父分区,可能导致倾斜
底层实现 调用 coalesce(n, shuffle=true) 默认调用 coalesce(n, shuffle=false)

2. 详细原理解析

repartition (重新分区)

  • 机制:它会触发全量的 Shuffle。Spark 会通过网络将数据在不同的 Executor 之间重新分发。
  • 数据分布:它通常使用 Round-Robin(轮询)或 Hash 算法将数据均匀地打散到新的分区中。
  • 场景
    1. 增加分区:当你需要更高的并行度时(例如,从 10 个分区变成 100 个)。
    2. 解决数据倾斜:如果你的某些分区数据量极大,某些极小,repartition 可以强制重新洗牌,让每个分区的数据量大致相等。
    3. 写入文件前:为了避免生成大量小文件,或者为了让输出文件大小均匀。

coalesce (合并分区)

  • 机制:它尽量避免 Shuffle。它通过将位于同一个 Executor 上的多个旧分区直接合并成一个新分区来实现。这是一个窄依赖(Narrow Dependency)操作。
  • 限制:如果不开启 shuffle 参数,coalesce 只能减少分区数,不能增加分区数(如果你尝试用 coalesce 增加分区,Spark 会忽略该请求,分区数保持不变)。
  • 场景
    1. 减少分区:最典型的场景是在 filter 操作之后。假设你过滤掉了 90% 的数据,原本 1000 个分区现在大部分是空的或很小,使用 coalesce(100) 可以将它们合并,提高后续处理效率,且无需网络传输。

3. 图解示例

假设你有 4 个分区(P1, P2, P3, P4),分布在 2 个节点上:

  • Node 1: P1, P2
  • Node 2: P3, P4

场景 A:使用 coalesce(2)
Spark 会尝试在本地合并:

  • Node 1: 将 P1 + P2 合并为 New_P1
  • Node 2: 将 P3 + P4 合并为 New_P2
  • 结果:没有数据跨节点传输,速度极快。

场景 B:使用 repartition(2)
Spark 会打乱所有数据:

  • P1 的一部分数据可能去 Node 2,P3 的一部分可能去 Node 1。
  • 结果:所有数据重新洗牌,保证 New_P1 和 New_P2 大小几乎完全一致。

4. 常见陷阱与最佳实践

陷阱 1:coalesce 可能导致并行度计算不均衡

由于 coalesce 只是简单地合并分区,如果原本的分区大小差异很大(例如 P1 有 100MB,P2 有 1MB),合并后可能会导致下游任务负载不均衡(有的 Task 处理很久,有的瞬间完成)。

  • 解决:如果数据严重倾斜,即使是减少分区,也建议使用 repartition 来强制平衡数据。

陷阱 2:想用 coalesce 增加分区

plaintext
rdd.coalesce(100) // 如果原分区是 10,这行代码无效,分区数仍为 10
  • 解决:必须使用 repartition(100) 或者 coalesce(100, shuffle=true)

最佳实践:写入数据库或文件

  • 写入 HDFS/S3:如果上游经过了大量的 filter,建议先 coalesce 减少分区数,避免产生数百万个 KB 级别的小文件(小文件是 Hadoop 生态系统的杀手)。
  • 写入并行度:如果写入数据库(如 JDBC),repartition 可以控制并发连接数。

总结一句话

如果你要减少分区数且不在乎数据是否绝对均匀,用 coalesce(性能好);如果你要增加分区数,或者必须让数据均匀分布,用 repartition

右滑查看面试常问