Spark java.lang.StackOverflowError

プロジェクトのgithubアドレス:bitcarmanlee easy-algorithm-interview-and-practice
誰もがスターを付け、メッセージを残し、一緒に学び、進歩することを歓迎します

1.発生した問題

実際のデータ分析プロセスでは、統計のために直近の年のデータを取得する必要があり、1年のデータは日ごとに分割されます。

val ymdSet = TimeUtils.genYmdSet(beginYmd, endYmd) // 获取过去一年时间的日期
var rdd = SparkIo.readThriftParquetFile(spark.sparkContext, pathxxx, classOf[xxx]) 

for(eachYmd <- ymdSet) {
  val tmppath = PathUtils.xxx + eachYmd
  val tmprdd = SparkIo.readThriftParquetFile(spark.sparkContext, tmppath, classOf[xxx])

  rdd = rdd.union(tmprdd)
}

rdd

上記のコードロジックは比較的明確です。日次データに従って一時的なrddを生成し、次にrddを元のrddに継続的に結合して、最終年のデータを取得します。

過去7日間のデータのみを分析対象として選択した場合、上記のコードは問題なく正常に実行できます。コードによって読み取られたデータが過去1年になると、例外がスローされます

ERROR executor.Executor: Exception in task 28.0 in stage 0.0 (TID 28)
java.lang.StackOverflowError
        at java.lang.Exception.<init>(Exception.java:102)
        at java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:89)
        at java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:72)
        at sun.reflect.GeneratedSerializationConstructorAccessor13.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
        at java.io.ObjectStreamClass.newInstance(ObjectStreamClass.java:967)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1782)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1706)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1344)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915)
        ......
        

2.理由分析

例外的な観点からは、java.io.ObjectInputStreamシリアル化を使用する場合の無限ループが原因です。
前の現象を組み合わせると、データが7日であれば問題ありませんが、1年間のデータファイル数が多すぎてスタック容量が不足するなどの理由で、1年間のデータが異常になります。継続的な結合プロセスにより、rddの系統が長くなりすぎ、最終的にスタックスペースが不足しました。和集合演算が実行されるたびに、系統のステップサイズが1ずつ増加するためです。

3.解決策

問題が特定されたので、解決策が出てきます。2つの方法しかありません
。1。スタックスペースを増やします。
2.系統の長さを減らします。

スタックスペースを増やすことは症状の解決策ですが、根本的な原因ではありません。クラスターのリソースは常に限られており、一度に処理するデータが多すぎることは常に隠れた危険であるため、2番目の解決策が最終的に採用されました。系統の長さを減らします。

特定の実装も比較的簡単です

def genrdd(startYmd: String, endYmd: String) = {
	val ymdSet = TimeUtils.genYmdSet(beginYmd, endYmd) // 获取过去一段时间的日期
	var rdd = SparkIo.readThriftParquetFile(spark.sparkContext, pathxxx, classOf[xxx]) 

	for(eachYmd <- ymdSet) {
  	val tmppath = PathUtils.xxx + eachYmd
  	val tmprdd = SparkIo.readThriftParquetFile(spark.sparkContext,tmppath,classOf[xxx])
  	
  	rdd = rdd.union(tmprdd)
}

rdd
}

まず、rddを生成するロジックをメソッドにカプセル化します。メソッドのパラメーターは、開始時間と終了時間です。
次に、たとえば1年の期間をそれぞれ3か月の4つのセグメントに分割し、それぞれ開始時刻と終了時刻を取得します。
最後に、メソッドを4回呼び出し、最後に結合すると、1年間のデータを正常にマージできます。

おすすめ

転載: blog.csdn.net/bitcarmanlee/article/details/110933910