web-dev-qa-db-ja.com

Apache Spark-foreach Vs foreachPartitions何を使用するか?

foreachPartitionsは、アキュムレータ変数に合計を実行するためにforeachを介して流れる場合を考慮したRDDメソッドと比較して、より高いレベルの並列処理により、パフォーマンスが向上するかどうかを知りたいと思います。

34

foreachおよびforeachPartitionsはアクションです。

foreach(関数):単位

副作用を伴う操作を呼び出すための汎用関数。 RDDの各要素に対して、渡されたfunctionを呼び出します。 これは通常、アキュムレーターの操作または外部ストアへの書き込みに使用されます。

注:foreach()の外部のアキュムレーター以外の変数を変更すると、未定義の動作が発生する場合があります。詳細については、 クロージャを理解する を参照してください。

scala> val accum = sc.longAccumulator("My Accumulator")
accum: org.Apache.spark.util.LongAccumulator = LongAccumulator(id: 0, name: Some(My Accumulator), value: 0)

scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s

scala> accum.value
res2: Long = 10

foreachPartition(function):ユニット

foreach()に似ていますが、各要素に対して関数を呼び出す代わりに、パーティションごとに呼び出します。関数は反復子を受け入れることができる必要があります。これは、mapPartitions()と同様に、関数呼び出しの回数を減らすため、foreach()よりも効率的です。

foreachPartitionの使用例:


  • 例1:使用するパーティションごとに1つのデータベース接続(各パーティションブロック内)で、これはscalaを使用して行う方法の使用例です。
/** 
 * foreachパーティションを使用してデータベースに挿入します。
 * 
 * @param sqlDatabaseConnectionString 
 * @param sqlTableName 
 */
 def insertToTable(sqlDatabaseConnectionString:String、sqlTableName:String):Unit = {
 
 // numPartitions =与えることを計画できる同時DB接続の数
 
 datframe.repartition(numofpartitionsyouwant)
 
 val tableHeader:String = dataFrame.columns.mkString( "、")
 dataFrame.foreachPartition {partition => 
 //注:各パーティションの1つの接続(より良い方法は、接続プールを使用することです)
 val sqlExecutorConnection:Connection = DriverManager.getConnection(sqlDatabaseConnectionString)
 //バッチサイズ1000一部のデータベースはexに1000を超えるバッチサイズを使用できないため、Azure sql 
 partition.grouped(1000).foreach {
 group => 
 val insertString:sca la.collection.mutable.StringBuilder = new scala.collection.mutable.StringBuilder()
 group.foreach {
 record => insertString.append( "( '" + record.mkString( "、 ")+" ')、 ")
} 
 
 sqlExecutorConnection.createStatement()
 .executeUpdate(f" INSERT INTO [$ sqlTableName]($ tableHeader)値 "
 + insertString.stripSuffix("、 "))
} 
 
 
 sqlExecutorConnection.close()//接続を閉じて、接続は枯渇しません。
} 
} 
  • 例2:

sparkstreaming(dstreams)とkafka producerforeachPartitionの使用法

dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
// only once per partition You can safely share a thread-safe Kafka //producer instance.
    val producer = createKafkaProducer()
    partitionOfRecords.foreach { message =>
      producer.send(message)
    }
    producer.close()
  }
}

注:パーティションごとにプロデューサーを作成するこの方法を避けたい場合は、Kafkaプロデューサーは非同期でバッファーであるため、sparkContext.broadcastを使用してプロデューサーをブロードキャストすることをお勧めします送信する前に大量のデータ。


アキュムレータは、スニペットを試してみて、それをいじってみます...それを通して、パフォーマンスをテストできます

 test( "Foreach-Spark"){
 import spark.implicits ._ 
 var accum = sc.longAccumulator 
 sc.parallelize(Seq(1,2 、3))。foreach(x => accum.add(x))
 assert(accum.value == 6L)
} 
 
 test( " Foreachパーティション-Spark "){
 import spark.implicits ._ 
 var accum = sc.longAccumulator 
 sc.parallelize(Seq(1,2,3))。foreachPartition( x => x.foreach(accum.add(_)))
 assert(accum.value == 6L)
} 

結論:

foreachPartitionパーティションの操作なので、明らかに[foreach

経験則:

foreachPartitionは、データベース接続やkafkaプロデューサーなど、要素ごとに1つではなくパーティションごとに1つを初期化する高価なリソース(foreach)にアクセスするときに使用する必要があります。アキュムレータに関しては、上記のテスト方法でパフォーマンスを測定できます。アキュムレータの場合も同様に高速に動作するはずです。

また... map vs mappartitions を参照してください。これは同様の概念を持っていますが、変換です。

22
Ram Ghadiyaram

foreachは、多くのノードでループを自動実行します。

ただし、各ノードでいくつかの操作を行いたい場合があります。たとえば、データベースに接続します。接続を確立してforeach関数に渡すことはできません。接続は1つのノードでのみ行われます。

したがって、foreachPartitionを使用すると、ループを実行する前に各ノードでデータベースに接続できます。

21
Bin Wang

foreachforeachPartitionsにはそれほど違いはありません。内部では、foreachが実行しているのは、提供された関数を使用してイテレーターのforeachを呼び出すことだけです。 foreachPartitionは、イテレータのループの外側で何かをする機会を与えてくれます。通常は、データベース接続をスピンアップするような高価なものや、それらの行に沿ったものです。したがって、各ノードのイテレータに対して一度だけ実行して、全体を通して再利用できるものがない場合は、foreachを使用して明快さと複雑さを軽減することをお勧めします。

16
Justin Pihony

foreachPartitionは、ノードごとのアクティビティではなく、パーティションごとに実行されることを意味するものではありません。パフォーマンスが低下する可能性がある場合、ノードの数に比べてパーティションの数が多くなる可能性があります。ノードレベルでアクティビティを実行する場合は、説明されているソリューション here が役立つかもしれませんが、私はテストしていません

4
deenbandhu

foreachPartitionは、パーティションごとに集約しているデータを反復処理する場合にのみ役立ちます。

良い例は、ユーザーごとのクリックストリームの処理です。ユーザーのイベントストリームを終了するたびに計算キャッシュをクリアしますが、ユーザーの行動の洞察を計算するために同じユーザーのレコード間で保持します。

3
Oren