私はそのような構造化されたストリームを実装しようとしました...
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回解析して何度も実行し、データベース接続を開いたままにする方法は?
基になるシンクの開閉は、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)
}
}
ライターのオープンとクローズは、ソースから生成されたバッチごとに試行されます。 open
とclose
を文字通り毎回シンクドライバーを開いたり閉じたりする場合は、実装を介して行う必要があります。
データの処理方法をより細かく制御したい場合は、バッチIDと基になるSink
を与える DataFrame
トレイトを実装できます。
trait Sink {
def addBatch(batchId: Long, data: DataFrame): Unit
}