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]
_です。
質問
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;;
_
前もって感謝します。
spark構造化ストリーミングのjoin operations
の状況は次のようになります。ストリーミングDataFrames
に参加できますstatic DataFrames
を使用して、さらに新しいstreaming DataFrames
を作成します。ただし、streaming
とouter 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)を介して起動されたソケットからデータを読み取ります。これにより、ストリームアプリのテストを行う際の作業が大幅に簡素化されます。このアプローチは、データのソースとしてKafka
とsocket
の両方でうまく機能します。
お役に立てば幸いです。
反対側のストリーミングデータセットとの外部結合 サポートされていません :
- ストリーミングと静的データセット間の外部結合は条件付きでサポートされています。
- ストリーミングデータセットとの完全外部結合はサポートされていません
- 右側のストリーミングデータセットとの左側の外部結合はサポートされていません
- 左側のストリーミングデータセットとの右外部結合はサポートされていません
他のDataset
が小さい場合は、Map
または同様の構造体broadcast
を使用して、UserDefinedFunction
内で参照できます。
val map: Broadcast[Map[T, U]] = ???
val lookup = udf((x: T) => map.value.get(x))
df.withColumn("foo", lookup($"_1"))