在集群中运行时,Spark Scala FoldLeft导致StackOverflow

时间:2018-09-04 07:04:02

标签: scala apache-spark stack-overflow foldleft

Im使用以下代码来使用此行的整形来重塑数据框。

数据框包含产品更改其ID的日期,但为了与包含交易的其他巨大数据框联接,我需要一个新列来定义有效ID范围。

例如,如果产品A更改为产品B的有效日期为01/01,然后更改为产品C的有效日期为03/01,则我需要在同一行中输入开始日期和结束日期,以便可以加入巨大的交易数据帧按产品有效日期为B(或C)的日期进行过滤,因此我可以将产品正确重命名为其有效实际ID。

另一条信息df_MPC大约有800行,并且不会增加很多。

所以我尝试的方法(在开发环境中运行时有效)是向左折叠。

MPC数据框的摘要版本为:

Product | Date      | NewProd
A       | 01/01/2018| B
B       | 03/01/2018| C

目标:

Product | Date      | NewProd | OriginalProd | EndDate
A       | 01/01/2018| B       | A            | 03/01
B       | 03/01/2018| C       | A            | 31/12-9999

(OriginalProd列对于与交易数据框的最终联接是必需的)

导致堆栈溢出的代码如下:

var rowList = new ListBuffer[Row]()
val it = df_MPC_SOURCE.toLocalIterator()
while (it.hasNext) { rowList += it.next()}

val df_MPC_TRANSFORMED = rowList.reverse
  .foldLeft(df_MPC_pre_edit_source: DataFrame)((acc, elem) => acc
    .withColumn("EndDate",
      when((col("N_DISTRIBUTOR_CODE") === elem.getAs("N_DISTRIBUTOR_CODE"))
        && col("N_CONTRACT_CODE") === elem.getAs("N_CONTRACT_CODE")
        && (col("N_PRODUCT_ID_NEW") === elem.getAs("N_PRODUCT_ID")),
        elem.getAs("D_EFFECTIVE_CHANGE"))
        .otherwise(col("EndDate")))
    .withColumn("OriginalProd",
      when((col("N_DISTRIBUTOR_CODE") === elem.getAs("N_DISTRIBUTOR_CODE"))
        && col("N_CONTRACT_CODE") === elem.getAs("N_CONTRACT_CODE")
        && (col("MPC_original") === elem.getAs("N_PRODUCT_ID_NEW")),
        elem.getAs("N_PRODUCT_ID"))
        .otherwise(col("OriginalProd")))
  )

此代码将源数据帧(上面提供的示例)转换为目标数据帧(上面也提供的示例)。

它通过以排序的方式(按日期)遍历其所有800行并针对其每一行来做到这一点:

  • 更改与给定行匹配的所有产品的有效日期
  • 如果我们发现中间产品,请更新原始产品ID 产品。例如,如果我们有一个产品从ID“ A”交换为 从“ B”到“ B”再到“ C”,我们将需要 能够加入我们的原始产品ID(在此情况下为“ A”) 原始交易记录表的结果,该表仅包含 产品ID“ A”。

以及在集群中使用此代码时引发的错误:

Exception in thread "main" java.lang.StackOverflowError
        at scala.collection.GenSetLike$class.apply(GenSetLike.scala:44)
        at scala.collection.AbstractSet.apply(Set.scala:47)
        at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$4$$anonfun$apply$11.apply(TreeNode.scala:334)
        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
        at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
        at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
        at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
        at scala.collection.AbstractTraversable.map(Traversable.scala:104)
        at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$4.apply(TreeNode.scala:333)
        at org.apache.spark.sql.catalyst.trees.TreeNode.mapProductIterator(TreeNode.scala:187)
        at org.apache.spark.sql.catalyst.trees.TreeNode.mapChildren(TreeNode.scala:304)
        at org.apache.spark.sql.catalyst.trees.TreeNode.transformDown(TreeNode.scala:272)
        at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$transformDown$1.apply(TreeNode.scala:272)
        at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$transformDown$1.apply(TreeNode.scala:272)
        at org.apache.spark.sql.catalyst.trees.TreeNode$$anonfun$4.apply(TreeNode.scala:306)
        at org.apache.spark.sql.catalyst.trees.TreeNode.mapProductIterator(TreeNode.scala:187)
        at org.apache.spark.sql.catalyst.trees.TreeNode.mapChildren(TreeNode.scala:304)

如何使此代码在群集中正常工作,使其与本地正常工作相同? 谢谢!

2 个答案:

答案 0 :(得分:2)

我花了一段时间才弄清楚你想做什么。我认为您可以使用更简单的方法来完成同样的操作。

这不能解释为什么您的代码不起作用,但是您的 foldleft 可以替换为spark sql查询,如下所示:

const state = {
   12344: {
      url: 'http://some-url.com',
      id: '12344'
   },
   12345: {
      url: 'http://some-other-url.com',
      id: '12345'
   }
}

const idToDelete = 12344

const { [idToDelete], ...newState } = state // dynamic key

console.log('newState:', newState)

// desired newState would only have the key 12345 and its value

希望这对您有所帮助。

答案 1 :(得分:1)

我将检查本地计算机和集群上Spark executor配置的差异。在本地计算机上创建的线程(任务/核心)数量可能少于在集群中的执行程序中创建的任务数量。减少每个执行程序的内核数将减少在执行程序jvm中创建的线程数,因此减少了线程堆栈占用的空间。另外,您可以尝试增加每个执行器的内存。最好在两台机器上保持执行程序的配置相同,然后查看问题是否再次出现。