web-dev-qa-db-ja.com

Spark構造化ストリーミング-静的データセットをストリーミングデータセットに結合する

Kafkaから読み取ったレコードを処理するために_Spark structured streaming_を使用しています。これが私が達成しようとしていることです:

(a)各レコードはタイプ_Tuple2_の_(Timestamp, DeviceId)_です。

(b)DeviceIdストリームに表示されると予想されるすべての有効なデバイスID(タイプKafka)のセットを含む静的_Dataset[DeviceId]_を作成しました。

(c)_Spark structured streaming_クエリを作成する必要があります

_ (i) Groups records by their timestamp into 5-minute windows
 (ii) For each window, get the list of valid device IDs that were **not** seen in that window
_

たとえば、すべての有効なデバイスIDのリストが_[A,B,C,D,E]_であり、特定の5分間のウィンドウのkafkaレコードにデバイスID _[A,B,E]_が含まれているとします。そのウィンドウの場合、私が探している見えないデバイスIDのリストは_[C,D]_です。

質問

  1. このクエリをSpark構造化ストリーミングでどのように記述できますか?Datasetが公開するexcept()メソッドとjoin()メソッドを使用してみました。ただし、どちらも_streaming Dataset_でサポートされていない操作を訴えるランタイム例外をスローしました。

これが私のコードのスニペットです:

_val validDeviceIds: Dataset[(DeviceId, Long)] = spark.createDataset[DeviceId](listOfAllDeviceIds.map(id => (id, 0L))) 

case class KafkaRecord(timestamp: TimestampType, deviceId: DeviceId)

// kafkaRecs is the data stream from Kafka - type is Dataset[KafkaRecord]
val deviceIdsSeen = kafkaRecs
     .withWatermark("timestamp", "5 minutes")
     .groupBy(window($"timestamp", "5 minutes", "5 minutes"), $"deviceId")
     .count()
     .map(row => (row.getLong(0), 1L))
     .as[(Long, Long)]

val unseenIds = deviceIdsSeen.join(validDeviceIds, Seq("_1"), "right_outer")
     .filter(row => row.isNullAt(1))
     .map(row => row.getLong(0))
_

最後のステートメントは、次の例外をスローします。

_Caused by: org.Apache.spark.sql.AnalysisException: Right outer join with a streaming DataFrame/Dataset on the left is not supported;;
_

前もって感謝します。

13
jithinpt

spark構造化ストリーミングのjoin operationsの状況は次のようになります。ストリーミングDataFramesに参加できますstatic DataFramesを使用して、さらに新しいstreaming DataFramesを作成します。ただし、streamingouter joinsの間のstatic Datasets条件付きはサポートされていますが、right/left joinsを含むstreaming Datasetは一般にサポートされていません構造化ストリーミング。その結果、ストリーミングデータセットと静的データセットを結合しようとしたときにスローされた AnalysisException に直面しました。私の言葉の証拠として、これでsparkのソースコードを見ることができます。 line 例外がスローされています。これは、試行した操作がサポートされていないことを示します。

静的なDataFramesを使用してstream of DataFramesで結合操作を実行しようとしました。

val streamingDf = sparkSession
    .readStream
    .format("kafka")
    .option("kafka.bootstrap.servers", "127.0.0.1:9092")
    .option("subscribe", "structured_topic")
    .load()

val lines = spark.readStream
      .format("socket")
      .option("Host", "localhost")
      .option("port", 9999)
      .load()

val staticDf = Seq((1507831462 , 100)).toDF("Timestamp", "DeviceId")

//Inner Join
streamingDf.join(staticDf, "Timestamp")
line.join(staticDf, "Timestamp")

//Left Join
streamingDf.join(staticDf, "Timestamp", "left_join")
line.join(staticDf, "Timestamp", "left_join")

ご覧のとおり、Kafkaからデータを消費することに加えて、nc(netcat)を介して起動されたソケットからデータを読み取ります。これにより、ストリームアプリのテストを行う際の作業が大幅に簡素化されます。このアプローチは、データのソースとしてKafkasocketの両方でうまく機能します。

お役に立てば幸いです。

4

反対側のストリーミングデータセットとの外部結合 サポートされていません

  • ストリーミングと静的データセット間の外部結合は条件付きでサポートされています。
    • ストリーミングデータセットとの完全外部結合はサポートされていません
    • 右側のストリーミングデータセットとの左側の外部結合はサポートされていません
    • 左側のストリーミングデータセットとの右外部結合はサポートされていません

他のDatasetが小さい場合は、Mapまたは同様の構造体broadcastを使用して、UserDefinedFunction内で参照できます。

val map: Broadcast[Map[T, U]] = ???
val lookup = udf((x: T) => map.value.get(x))

df.withColumn("foo", lookup($"_1"))
1
user8762155