top of page

如何优化 Apache Spark 作业以防止过度改组

已更新:2天前

在使用 Apache Spark 时,我经常遇到一个常见却又棘手的性能问题:过度的 shuffle。shuffle 会显著降低应用程序的运行速度,因此软件工程师必须找到有效的方法来优化 Spark 作业。通过实践经验和各种技巧,我发现了几种可以显著减少 shuffle 并提升 Spark 作业性能的策略。

了解 Apache Spark 中的 Shuffle


Apache Spark 中的 Shuffle 发生在跨分区重新分配数据时,这通常是由于“groupBy”、“join”或“reduceByKey”等操作造成的。虽然 Shuffle 对于某些操作来说是必要的,但过度 Shuffle 会导致显著的性能损失。


Shuffle 操作耗费大量资源。例如,与在内存中处理数据相比,网络和磁盘的输入/输出 (I/O) 速度可能要慢得多。根据 Databricks 的数据,如果管理不善,Shuffle 操作可能会消耗高达50% 的集群资源。理解 Shuffle 操作的影响促使我探索各种优化技术,以最大限度地减少其使用。


分区的作用


我实施的首批策略之一是改进数据分区方式。默认情况下,Spark 会创建一定数量的分区,这通常会导致数据分布不均匀。这种不平衡会增加 shuffle 的概率。


为了优化 shuffle,我专注于实现自定义分区。例如,在将输出数据写入磁盘时使用 `partitionBy` 方法有助于对经常一起使用的键进行聚类。这种做法在我的项目中将 shuffle 次数减少了30% ,从而确保对这些键的后续操作减少节点间数据移动。


利用 `reduceByKey` 而不是 `groupByKey`


我的优化工作中的另一个关键步骤是选择“reduceByKey”而不是“groupByKey”。


“groupByKey”操作会收集给定键的所有值,这可能会导致集群中大量的数据移动。然而,“reduceByKey”在shuffle阶段执行聚合操作,从而减少了需要跨节点移动的数据。在我的实现中,从“groupByKey”切换到“reduceByKey”后,专注于数据聚合的作业的性能提升了近40% 。这个小小的调整带来了显著的效果。


使用广播变量


在处理连接期间经常访问的小型查找表时,我发现可以通过使用广播变量来减少混洗。


广播使 Spark 能够将只读变量发送到集群内的所有节点。通过使用广播变量进行查找,而不是对大型数据集进行混排,我可以消除不必要的开销。这种策略将混排次数减少了多达25% ,从而显著节省了时间并提高了资源效率。


调整 Spark 配置


配置 Spark 设置是降低 shuffle 并提高性能的另一种有效方法。我重点关注以下具体设置:


  1. spark.sql.shuffle.partitions :默认设置为200 。对于较小的数据集,降低此数字可以最大限度地减少 shuffle。


  2. spark.default.parallelism :根据集群的核心数调整此设置可以更高效地执行任务,而无需不必要的改组。


  3. 内存管理:分配正确的内存(例如 `spark.executor.memory`)至关重要。合理的内存设置可以最大限度地减少磁盘溢出,从而有助于减少 shuffle。


通过根据我的集群需求微调这些配置,我有效地减少了过多的改组,从而显著提高了性能。


缓存中间结果


我还了解到在适用的情况下缓存中间结果的重要性。使用 `cache()` 或 `persist()` 方法可以存储操作结果,以便稍后重用。


通过缓存结果,我避免了多次重新计算或重排相同的数据。在一个项目中,这种策略节省了宝贵的计算时间和资源,使性能提升了20%


最后的想法


优化 Apache Spark 作业以防止过度 shuffle 需要多种策略和周密的规划。通过自定义分区、选择合适的运算符、利用广播变量、调整配置以及缓存结果等多种方式,我成功减少了 Spark 作业中的 shuffle 操作。


这些优化不仅提升了性能,还提高了资源利用效率。对于软件工程师来说,提高大数据处理任务的效率至关重要。我希望通过分享这些见解,帮助其他人简化他们的 Spark 作业,从而提高性能并减少 shuffle。



分布式计算环境的广角视图
A uniform distribution in a computing environment can reduce excessive shuffling.

bottom of page