如何计算合并的最佳numberOfPartitions?

时间:2016-11-29 11:56:48

标签: scala apache-spark rdd

所以,我理解一般情况下应该在以下时间使用coalesce()

  

由于filter或其他可能导致缩小原始数据集(RDD,DF)的操作,分区数减少。在过滤掉大型数据集后,coalesce()对于更有效地运行操作非常有用。

我也明白它比repartition便宜,因为它只在必要时通过移动数据来减少混乱。我的问题是如何定义coalesce占用的参数(idealPartionionNo)。我正在研究一个项目,该项目是从另一位工程师传递给我的,他使用下面的计算来计算该参数的值。

// DEFINE OPTIMAL PARTITION NUMBER
implicit val NO_OF_EXECUTOR_INSTANCES = sc.getConf.getInt("spark.executor.instances", 5)
implicit val NO_OF_EXECUTOR_CORES = sc.getConf.getInt("spark.executor.cores", 2)

val idealPartionionNo = NO_OF_EXECUTOR_INSTANCES * NO_OF_EXECUTOR_CORES * REPARTITION_FACTOR

然后将其与partitioner对象一起使用:

val partitioner = new HashPartitioner(idealPartionionNo)

但也用于:

RDD.filter(x=>x._3<30).coalesce(idealPartionionNo)

这是正确的做法吗? idealPartionionNo值计算背后的主要思想是什么?什么是REPARTITION_FACTOR?我一般如何定义它?

此外,由于YARN负责即时识别可用的执行程序,因此有一种方法可以即时获取该数字(AVAILABLE_EXECUTOR_INSTANCES)并将其用于计算idealPartionionNo(即替换{{ 1}}与NO_OF_EXECUTOR_INSTANCES)?

理想情况下,表格的一些实际例子:

  • 这是数据集 size );
  • 这是RDD / DF的一些转换和可能的重用。
  • 这是您应该重新分区/合并的地方。
  • 假设AVAILABLE_EXECUTOR_INSTANCES 执行者 n 核心分区因子等于{{1} }

然后:

  • 理想的分区数量为==&gt; ???

另外,如果你能引用我一个很好的博客来解释这些,我会非常感激。

3 个答案:

答案 0 :(得分:12)

在实践中,最佳分区数更多地取决于您拥有的数据,您使用的转换以及整体配置,而不是可用资源。

  • 如果分区数太少,您将遇到长时间的GC暂停,不同类型的内存问题,以及最后资源利用率不佳。
  • 如果分区数太高,则维护成本很容易超过处理成本。此外,如果您使用非分布式缩减操作(如reduce而不是treeReduce),则大量分区会导致驱动程序的负载增加。

您可以找到一些规则,这些规则建议超额订阅分区与核心数量(因子2或3似乎很常见)或将分区保持在一定的大小,但这并未考虑您自己的代码:

  • 如果你分配很多,你可以期待长时间的GC暂停,并且最好使用较小的分区。
  • 如果某段代码很贵,那么您的随机播放成本可以通过更高的并发性来分摊。
  • 如果您有过滤器,您可以根据谓词的判别力调整分区数(如果您希望保留5%的数据和99%的数据,则做出不同的决定。)

在我看来:

  • 使用一次性作业保持较高的数字分区以保持安全(较慢比失败更好)。
  • 可重复使用的作业以保守配置开始,然后执行 - monitor - adjust configuration - repeat。
  • 请勿尝试根据执行程序或核心数使用固定数量的分区。首先要了解您的数据和代码,然后调整配置以反映您的理解。

    通常,确定群集表现出稳定行为的每个分区的原始数据量相对容易(根据我的经验,它在几百兆字节的范围内,具体取决于您使用的格式,数据结构)加载数据和配置)。这是&#34;魔术数字&#34;你正在寻找。

一般情况下你需要记住的一些事情:

  • 分区数量并不一定反映 数据分发。任何需要随机播放的操作(*byKeyjoinRDD.partitionByDataset.repartition)都可能导致数据分布不均匀。始终监控您的工作是否存在重大数据倾斜的症状。
  • 一般分区数不是常数。具有多个依赖项(unioncoGroupjoin)的任何操作都会影响分区数。

答案 1 :(得分:7)

您的问题是有效的,但Spark分区优化依赖于完全您正在运行的计算。你需要有充分的理由重新分配/合并;如果你只是计算一个RDD(即使它有大量稀疏填充的分区),那么任何重新分区/合并步骤都会让你慢下来。

重新分区与合并

repartition(n)(与coalesce(n, shuffle = true)coalesce(n, shuffle = false)相同的区别与执行模型有关.shuffle模型采用原始RDD中的每个分区,随机发送其数据绕过所有执行程序,并使用新的(更小或更大)分区数生成RDD。无混乱模型创建一个新的RDD,它将多个分区作为一个任务加载。

让我们考虑一下这个计算:

sc.textFile("massive_file.txt")
  .filter(sparseFilterFunction) // leaves only 0.1% of the lines
  .coalesce(numPartitions, shuffle = shuffle)

如果shuffletrue,那么文本文件/过滤器计算会在textFile中的默认值给出的许多任务中发生,并且微小的过滤结果会被洗牌。如果shufflefalse,则总任务数最多为numPartitions

如果numPartitions为1,则差异非常明显。 shuffle模型将并行处理和过滤数据,然后将0.1%的过滤结果发送到一个执行器以进行下游DAG操作。 no-shuffle模型将从一开始就在一个核心上处理和过滤数据。

采取步骤

考虑下游操作。如果您只使用此数据集一次,那么您可能根本不需要重新分区。如果要保存过滤的RDD以供以后使用(例如,到磁盘),请考虑上面的权衡。熟悉这些模型需要经验,当一个表现更好时,请尝试两种方式,看看它们的表现如何!

答案 2 :(得分:3)

正如其他人所回答的,没有计算出你要求的公式。也就是说,你可以对第一部分进行有根据的猜测,然后随着时间的推移进行微调。

第一步是确保您有足够的分区。如果每个执行程序都有NO_OF_EXECUTOR_INSTANCES执行程序和NO_OF_EXECUTOR_CORES核心,那么您可以同时处理NO_OF_EXECUTOR_INSTANCES * NO_OF_EXECUTOR_CORES分区(每个分区将转到特定实例的特定核心)。 也就是说,假设所有内容在核心之间平均分配,并且所有内容都需要完全相同的时间来处理。这种情况很少发生。由于本地性(例如,数据需要来自不同的节点),或者仅仅因为它们不平衡(例如,如果您的数据由根域分区,则包括分区在内),其中一些很可能会先于其他地方完成谷歌可能会很大)。这是REPARTITION_FACTOR发挥作用的地方。我们的想法是,我们“覆盖”每个核心,因此如果一个人完成得非常快,一个人完成得很慢,我们可以选择在他们之间划分任务。 2-3的因素通常是一个好主意。

现在让我们来看看单个分区的大小。假设您的整个数据大小为X MB,并且您有N个分区。每个分区平均为X / N MB。如果N相对于X较大,那么您可能具有非常小的平均分区大小(例如几KB)。在这种情况下,降低N通常是一个好主意,因为管理每个分区的开销变得太高。另一方面,如果大小非常大(例如几GB),那么你需要同时保存大量数据,这会导致诸如垃圾收集,高内存使用等问题。

最佳尺寸是一个很好的问题,但一般人们似乎更喜欢100-1000MB的分区,但事实上几十MB也可能是好的。

您应该注意的另一件事是计算分区的更改方式。例如,假设您从1000个分区开始,每个分区100MB,但随后过滤数据,以便每个分区变为1K,那么您可能应该合并。执行groupby或join时可能会发生类似问题。在这种情况下,分区的大小和分区的数量都会发生变化,并且可能会达到不合适的大小。