Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/6.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.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 当DF有太多列时,每个记录多次调用Spark UDF_Scala_Apache Spark_Apache Spark Sql - Fatal编程技术网

Scala 当DF有太多列时,每个记录多次调用Spark UDF

Scala 当DF有太多列时,每个记录多次调用Spark UDF,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我在使用Spark 1.6.1时遇到了一个奇怪的行为:我在一个包含一些输入数据的数据框上运行一个UDF,进行一些繁重的计算(物理模拟),并构建一个包含许多列(~40)的结果数据框 奇怪的是,在这种情况下,我的UDF在我的输入数据帧的每个记录中被调用了不止一次(频率是1.6倍),我觉得这是不可接受的,因为它非常昂贵。如果我减少列数(例如,减少到20列),则此行为将消失 我写下了一个小脚本,演示了这一点: import org.apache.spark.sql.SQLContext import o

我在使用Spark 1.6.1时遇到了一个奇怪的行为:我在一个包含一些输入数据的数据框上运行一个UDF,进行一些繁重的计算(物理模拟),并构建一个包含许多列(~40)的结果数据框

奇怪的是,在这种情况下,我的UDF在我的输入数据帧的每个记录中被调用了不止一次(频率是1.6倍),我觉得这是不可接受的,因为它非常昂贵。如果我减少列数(例如,减少到20列),则此行为将消失

我写下了一个小脚本,演示了这一点:

import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.functions.udf


object Demo {

  case class Result(a: Double)

  def main(args: Array[String]): Unit = {

    val sc = new SparkContext(new SparkConf().setAppName("Demo").setMaster("local[*]"))
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._

    val numRuns = sc.accumulator(0) // to count the number of udf calls

    val myUdf = udf((i:Int) => {numRuns.add(1);Result(i.toDouble)})

    val data = sc.parallelize((1 to 100), numSlices = 5).toDF("id")

    // get results of UDF
    var results = data
      .withColumn("tmp", myUdf($"id"))
      .withColumn("result", $"tmp.a")


    // add many columns to dataframe (must depend on the UDF's result)
    for (i <- 1 to 42) {
      results=results.withColumn(s"col_$i",$"result")
    }

    // trigger action
    val res = results.collect()
    println(res.size) // prints 100

    println(numRuns.value) // prints 160

  }
}
import org.apache.spark.sql.SQLContext
导入org.apache.spark.{SparkConf,SparkContext}
导入org.apache.spark.sql.functions.udf
对象演示{
案例等级结果(a:双)
def main(参数:数组[字符串]):单位={
val sc=new SparkContext(new SparkConf().setAppName(“Demo”).setMaster(“local[*]))
val sqlContext=新的sqlContext(sc)
导入sqlContext.implicits_
val numRuns=sc.acculator(0)//计算udf调用的数量
val myUdf=udf((i:Int)=>{numRuns.add(1);Result(i.toDouble)})
val data=sc.parallelize((1到100),numSlices=5.toDF(“id”)
//获取UDF的结果
var结果=数据
.withColumn(“tmp”,myUdf($“id”))
.withColumn(“结果“,$“tmp.a”)
//向dataframe添加许多列(必须取决于UDF的结果)

对于(i我不能真正解释这种行为,但很明显,查询计划以某种方式选择了一条路径,其中一些记录被计算了两次。这意味着,如果我们缓存中间结果(就在应用UDF之后),我们可能能够“强制”Spark不会重新计算UDF。事实上,一旦添加缓存,它的行为与预期一样-UDF被精确调用100次:

// get results of UDF
var results = data
  .withColumn("tmp", myUdf($"id"))
  .withColumn("result", $"tmp.a").cache()

当然,缓存有其自身的成本(内存…),但如果它能节省许多UDF调用,那么它最终可能会对您有所帮助。

大约一年前,我们也遇到过同样的问题,并且花了很多时间,直到我们最终找出问题所在

我们还有一个非常昂贵的UDF需要计算,我们发现每次我们参考它的专栏时,它都会被反复计算。几天前我们又遇到了这种情况,所以我决定打开一个错误:

当时我们也在这里提出了一个问题,但现在我看到标题不太好:

我同意Tzach关于以某种方式“强制”计划计算UDF的观点。我们做得更糟糕,但我们不得不这样做,因为我们无法缓存()数据-数据太大了:

val df = data.withColumn("tmp", myUdf($"id"))
val results = sqlContext.createDataFrame(df.rdd, df.schema)
             .withColumn("result", $"tmp.a")
更新:

现在我看到我的jira票证链接到了另一张:,这张票证仍然没有以正确的方式处理这个问题,但它提供了一个可选的解决方法:

val results = data.withColumn("tmp", explode(array(myUdf($"id"))))
                  .withColumn("result", $"tmp.a")
在较新的spark verion(2.3+)中,我们可以将UDF标记为非确定性:

i、 e.使用

val myUdf = udf(...).asNondeterministic()

这确保了UDF只被调用一次

这确实有效!我仍然等待着接受答案,也许有人有一个全面的答案是的,我也很好奇-完全可以,你不接受:)