Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 合并降低了整个级的平行度(spark)_Scala_Apache Spark - Fatal编程技术网

Scala 合并降低了整个级的平行度(spark)

Scala 合并降低了整个级的平行度(spark),scala,apache-spark,Scala,Apache Spark,有时Spark以低效的方式“优化”数据帧计划。考虑火花2.1中的以下例子(也可以在火花1.6中再现): 在这个例子中,我想在一个昂贵的数据帧转换之后写一个文件(这只是一个演示这个问题的例子)。Spark将合并(1)向上移动,使得UDF仅应用于包含1个分区的数据帧,从而破坏并行性(有趣的是重新分区(1)不会以这种方式运行) 一般来说,当我想在转换的某个部分增加并行性,但在转换之后降低并行性时,就会发生这种行为 我发现了一种解决方法,包括缓存数据帧,然后触发对数据帧的完整评估: val df = s

有时Spark以低效的方式“优化”数据帧计划。考虑火花2.1中的以下例子(也可以在火花1.6中再现):

在这个例子中,我想在一个昂贵的数据帧转换之后写一个文件(这只是一个演示这个问题的例子)。Spark将
合并(1)
向上移动,使得UDF仅应用于包含1个分区的数据帧,从而破坏并行性(有趣的是
重新分区(1)
不会以这种方式运行)

一般来说,当我想在转换的某个部分增加并行性,但在转换之后降低并行性时,就会发生这种行为

我发现了一种解决方法,包括缓存数据帧,然后触发对数据帧的完整评估:

val df = sparkContext.parallelize((1 to 500).map(i=> scala.util.Random.nextDouble),100).toDF("value")

val expensiveUDF = udf((d:Double) => {Thread.sleep(100);d})

val df_result = df
.withColumn("udfResult",expensiveUDF($"value"))
.cache

df_result.rdd.count // trigger computation

df_result
.coalesce(1)
.saveAsTable(tablename)

我的问题是:有没有其他方法告诉Spark在这种情况下不要降低并行性?

事实上,这并不是因为SparkSQL的优化,SparkSQL不会改变Coalesce操作符的位置,正如执行的计划所示:

Coalesce 1
+- *Project [value#2, UDF(value#2) AS udfResult#11]
   +- *SerializeFromObject [input[0, double, false] AS value#2]
      +- Scan ExternalRDDScan[obj#1]
我引用了coalesce API描述中的一段:

注:本段由jira SPARK-19399增补。因此,它不应该出现在2.0的API中

但是,如果您正在进行激烈的合并,例如,合并到numPartitions= 1,这可能会导致计算在较少的节点上进行 比您喜欢的多(例如,如果numPartitions=1,则为一个节点)。到 为了避免这种情况,可以调用重新分区。这将添加一个洗牌步骤, 但这意味着当前的上游分区将并行执行 (无论当前分区是什么)

coalesce API不执行洗牌,但会导致以前的RDD和当前RDD之间存在狭窄的依赖关系。由于RDD是惰性计算,因此计算实际上是通过合并分区完成的


为了防止这种情况发生,您应该使用重新分区API。

简而言之,您希望实例化一个包含500个分区的RDD,然后实例化另一个RDD,将结果合并到一个分区中,这样您就可以将其保存到一个文件中——cf.>>wild guess:可能是对
getNumPartitions()的简单调用
将足以强制实例化,而不必使用
count()
实际扫描结果。@SamsonScharfrichter不调用
getNumPartitions()
是不够的,也不会阻止合并被“推上”巧合:我刚刚偶然发现了这个演示,从最近的Spark Summit>来看,这似乎是一个非常做作的例子,您是否真的经历过这种情况?在UDF执行后使用类似reduceByKey的东西来分解它怎么样?请您详细说明引用的@viirya段落?这一段落是否暗示“在较少节点上进行的计算”就是UDF执行?从执行计划来看,情况并非如此,除非您建议合并减少上游节点的数量,这似乎不合逻辑。
Coalesce 1
+- *Project [value#2, UDF(value#2) AS udfResult#11]
   +- *SerializeFromObject [input[0, double, false] AS value#2]
      +- Scan ExternalRDDScan[obj#1]