web-dev-qa-db-ja.com

Spark構造化ストリーミングForeachWriterとデータベースのパフォーマンス

私はそのような構造化されたストリームを実装しようとしました...

myDataSet
  .map(r =>  StatementWrapper.Transform(r))
  .writeStream
  .foreach(MyWrapper.myWriter)
  .start()
  .awaitTermination()

これはすべて機能しているように見えますが、MyWrapper.myWriterのスループットを見るのは恐ろしいことです。それは事実上JDBCシンクになろうとしています。次のようになります。

val myWriter: ForeachWriter[Seq[String]] = new ForeachWriter[Seq[String]] {

  var connection: Connection = _

  override def open(partitionId: Long, version: Long): Boolean = {
    Try (connection = getRemoteConnection).isSuccess
  }

  override def process(row: Seq[String]) {
    val statement = connection.createStatement()
    try {
      row.foreach( s => statement.execute(s) )
    } catch {
      case e: SQLSyntaxErrorException => println(e)
      case e: SQLException => println(e)
    } finally {
      statement.closeOnCompletion()
    }
  }

  override def close(errorOrNull: Throwable) {
    connection.close()
  }
}

だから私の質問は-新しいForeachWriterは各行に対してインスタンス化されていますか?したがって、open()とclose()は、データセットのすべての行に対して呼び出されますか?

スループットを改善するためのより良い設計はありますか?

SQLステートメントを1回解析して何度も実行し、データベース接続を開いたままにする方法は?

12
Exie

基になるシンクの開閉は、ForeachWriterの-​​実装によって異なります。

ForeachWriterを呼び出す関連クラスは ForeachSink であり、これはライターを呼び出すコードです。

data.queryExecution.toRdd.foreachPartition { iter =>
  if (writer.open(TaskContext.getPartitionId(), batchId)) {
    try {
      while (iter.hasNext) {
        writer.process(encoder.fromRow(iter.next()))
      }
    } catch {
      case e: Throwable =>
        writer.close(e)
        throw e
    }
    writer.close(null)
  } else {
    writer.close(null)
  }
}

ライターのオープンとクローズは、ソースから生成されたバッチごとに試行されます。 opencloseを文字通り毎回シンクドライバーを開いたり閉じたりする場合は、実装を介して行う必要があります。

データの処理方法をより細かく制御したい場合は、バッチIDと基になるSinkを与える DataFrame トレイトを実装できます。

trait Sink {
  def addBatch(batchId: Long, data: DataFrame): Unit
}
11
Yuval Itzchakov