web-dev-qa-db-ja.com

長いRDDリネージュによるStackoverflow

HDFSには何千もの小さなファイルがあります。ファイルのわずかに小さいサブセット(これも数千単位)を処理する必要があります。fileListには、処理する必要のあるファイルパスのリストが含まれています。

// fileList == list of filepaths in HDFS

var masterRDD: org.Apache.spark.rdd.RDD[(String, String)] = sparkContext.emptyRDD

for (i <- 0 to fileList.size() - 1) {

val filePath = fileStatus.get(i)
val fileRDD = sparkContext.textFile(filePath)
val sampleRDD = fileRDD.filter(line => line.startsWith("#####")).map(line => (filePath, line)) 

masterRDD = masterRDD.union(sampleRDD)

}

masterRDD.first()

//ループから外れると、アクションを実行すると、RDDの系統が長いためにスタックオーバーフローエラーが発生します

Exception in thread "main" Java.lang.StackOverflowError
    at scala.runtime.AbstractFunction1.<init>(AbstractFunction1.scala:12)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.<init>(UnionRDD.scala:66)
    at org.Apache.spark.rdd.UnionRDD.getPartitions(UnionRDD.scala:66)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
    at scala.Option.getOrElse(Option.scala:120)
    at org.Apache.spark.rdd.RDD.partitions(RDD.scala:237)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
    at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:34)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at org.Apache.spark.rdd.UnionRDD.getPartitions(UnionRDD.scala:66)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
    at scala.Option.getOrElse(Option.scala:120)
    at org.Apache.spark.rdd.RDD.partitions(RDD.scala:237)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
    at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:34)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at org.Apache.spark.rdd.UnionRDD.getPartitions(UnionRDD.scala:66)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
    at org.Apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
    at scala.Option.getOrElse(Option.scala:120)
    at org.Apache.spark.rdd.RDD.partitions(RDD.scala:237)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    at org.Apache.spark.rdd.UnionRDD$$anonfun$1.apply(UnionRDD.scala:66)
    =====================================================================
    =====================================================================
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
17
jdprasad

一般に、チェックポイントを使用して長い系統を壊すことができます。これに多かれ少なかれ似ているものが機能するはずです:

import org.Apache.spark.rdd.RDD
import scala.reflect.ClassTag

val checkpointInterval: Int = ???

def loadAndFilter(path: String) = sc.textFile(path)
  .filter(_.startsWith("#####"))
  .map((path, _))

def mergeWithLocalCheckpoint[T: ClassTag](interval: Int)
  (acc: RDD[T], xi: (RDD[T], Int)) = {
    if(xi._2 % interval == 0 & xi._2 > 0) xi._1.union(acc).localCheckpoint
    else xi._1.union(acc)
  }

val zero: RDD[(String, String)] = sc.emptyRDD[(String, String)]
fileList.map(loadAndFilter).zipWithIndex
  .foldLeft(zero)(mergeWithLocalCheckpoint(checkpointInterval))

この特定の状況では、はるかに簡単な解決策はSparkContext.unionメソッドを使用することです。

val masterRDD = sc.union(
  fileList.map(path => sc.textFile(path)
    .filter(_.startsWith("#####"))
    .map((path, _))) 
)

ループ/ reduceによって生成されたDAGを見ると、これらのメソッドの違いは明らかです。

enter image description here

および単一のunion

enter image description here

もちろん、ファイルが小さい場合は、wholeTextFilesflatMapを組み合わせて、すべてのファイルを一度に読み取ることができます。

sc.wholeTextFiles(fileList.mkString(","))
  .flatMap{case (path, text) =>  
    text.split("\n").filter(_.startsWith("#####")).map((path, _))}
32
zero323