structured-Streaming之watermark水印update和append模式學習

 

 

http://spark.apache.org/docs/2.4.3/structured-streaming-programming-guide.html

建議看官網,官網最權威

注意點

1、該outputMode爲update模式,即只會輸出那些有更新的數據!!

2、該開窗窗口長度爲10min,步長5min,水印爲eventtime-10min,(需理解開窗規則)

3、官網案例trigger(Trigger.ProcessingTime("5 minutes")),但是測試的時候不建議使用這個

4、未輸出數據不代表已經在內存中被剔除,只是由於update模式的原因

5、建議比對append理解水印

個人測試案例

object WaterMarkUpdate {
  def main(args: Array[String]): Unit = {
   val spark: SparkSession = SparkSession.builder().appName("chenchi").master("local[2]").getOrCreate()
       import  spark.implicits._
       spark.readStream
         .format("socket")
         .option("host", "hadoop102")
         .option("port", "1234")
        // .option("includeTimestamp", true) // 給產生的數據自動添加時間戳
         .load()
         .as[String]
         .map(x=>{
            val split: Array[String] = x.split(",")
             (split(0),Timestamp.valueOf(split(1)))
           })
         .toDF("word", "time")
         .withWatermark("time","10 minutes")
         .groupBy(
           window($"time", "10 minutes", "5 minutes"),
           $"word"
         )
         .count()
         .writeStream
         .format("console")
         .outputMode("update")
         //.trigger(Trigger.ProcessingTime("5 minutes"))
         .option("truncate","false")
         .start()
         .awaitTermination()
  }
}

測試數據和官網一樣

dog,2019-09-25 12:07:00
owl,2019-09-25 12:08:00


dog,2019-09-25 12:14:00
cat,2019-09-25 12:09:00

cat,2019-09-25 12:15:00
dog,2019-09-25 12:08:00
owl,2019-09-25 12:13:00
owl,2019-09-25 12:21:00

owl,2019-09-25 12:17:00

第一次輸入

dog,2019-09-25 12:07:00
owl,2019-09-25 12:08:00

得到結果

這個主要是因爲我輸入兩條數據的時候兩條數據時間有間隔,所以會出現兩次,兩次結果合起來和官網一樣,

batch2輸出的是因爲update模式,不出輸出dog,因爲dog中的內容沒有更新

第二次輸入

 與官網一樣

第三次輸入數據

cat,2019-09-25 12:15:00
dog,2019-09-25 12:08:00
owl,2019-09-25 12:13:00
owl,2019-09-25 12:21:00

這個又是因爲trigger時間沒有設置,屬於儘快模式,只輸出更新內容,官網那裏12:20下面那裏.....省略了12:21owl這條數據的輸出,這個不是我錯了,

其實更新模式很簡單,都不想寫了再堅持一下。

第四次入內容

owl,2019-09-25 12:17:00

這個也是官網省略了一條記錄,不是我這裏多了一條記錄,到這裏明白髮現我還是錯了,就用那個trigger設置一下時間好點,這樣顯示結果容易觀看,

其實個人覺得update模式有弊端,

1、數據已經被剔除,比如原先內存已經存了12:00-12:10 ,dog ,5 ,此時內存裏有5個dog,但是到後面水印比如爲12:20,根據水印,這個5個dog已經過期會在內存裏被剔除,但是我新增一條數據 dog,12:24,結果會顯示dog:6,這樣根本看不出數據是否過期

當然有可能是我的想法太狹隘,感覺不到這個的好處

————————————————————————————————————————————————————————

下面我將解釋下append模式下的 watermark作用

上述代碼修改下

.outputMode("append")
.trigger(Trigger.ProcessingTime("3 minutes"))

測試數據

dog,2019-09-25 12:07:00
owl,2019-09-25 12:08:00

dog,2019-09-25 12:14:00
cat,2019-09-25 12:09:00

cat,2019-09-25 12:15:00
dog,2019-09-25 12:08:00
owl,2019-09-25 12:13:00
owl,2019-09-25 12:21:00

donkey,2019-09-25 12:04:00
owl,2019-09-25 12:26:00
owl,2019-09-25 12:17:00

cat,2019-09-25 12:09:00

還是之前的數據

第一次輸入

dog,2019-09-25 12:07:00
owl,2019-09-25 12:08:00

就是空數據!!官網也爲空

第二次輸入

dog,2019-09-25 12:14:00
cat,2019-09-25 12:09:00

沒錯還是空數據,官網也是空, 這又是爲啥,稍後再說

第三次輸入

cat,2019-09-25 12:15:00
dog,2019-09-25 12:08:00
owl,2019-09-25 12:13:00
owl,2019-09-25 12:21:00


這裏輸出了兩次

這裏要說下,console輸出的時候,有時候你輸入一次,一下出來兩個batch?。我覺得是你輸入一次數據(一條數據或者一批),一批數據可能存在時間差,處理的時間不一樣,就會出現多個batch,不知道我的猜想對不對

第四次輸入

donkey,2019-09-25 12:04:00
owl,2019-09-25 12:26:00
owl,2019-09-25 12:17:00

這裏出現數據差異的原因

第五次輸入

cat,2019-09-25 12:09:00

batch=null

附上一張統計表,比較直觀點

  數據內容 此時內存中的數據有 控制檯輸出的結果 解析
第一次輸入的數據 dog,2019-09-25 12:07:00
owl,2019-09-25 12:08:00
12:00-12:10  dog 1
12:00-12:10  owl 1
12:05-12:15  dog 1
12:05-12:15  owl 1
null 無內存中的數據剔除
第二次輸入的數據 dog,2019-09-25 12:14:00
cat,2019-09-25 12:09:00
12:00-12:10  dog 1
12:00-12:10  owl 1
12:00-12:10  cat 1
12:05-12:15  dog 2
12:05-12:15  owl 1
12:05-12:15  cat 1
12:10-12:20  dog 1
null 無內存中的數據剔除
第三次輸入的數據 cat,2019-09-25 12:15:00
dog,2019-09-25 12:08:00
owl,2019-09-25 12:13:00
owl,2019-09-25 12:21:00
12:00-12:10  dog 2
12:00-12:10  owl 1
12:00-12:10  cat 1

12:05-12:15  dog 3
12:05-12:15  owl 2
12:05-12:15  cat 1 注意這個
12:10-12:20  dog 1
12:10-12:20  cat 1
12:10-12:20  owl 1
12:15-12:25  cat 1
12:15-12:25  owl 1
12:20-12:30  owl 1
batch3:null和
Batch: 4

+------------------------------------------+----+-----+
|window                                    |word|count|
+------------------------------------------+----+-----+
|[2019-09-25 12:00:00, 2019-09-25 12:10:00]|dog |2    |
|[2019-09-25 12:00:00, 2019-09-25 12:10:00]|owl |1    |
|[2019-09-25 12:00:00, 2019-09-25 12:10:00]|cat |1    |
+------------------------------------------+----+-----+

此時這裏的watermarke爲12:21-10=12:11
這裏輸出了兩段一段爲空
一段中爲小於12:11的所有數據,即表明這個watermark起了作用
第四次輸入的數據 donkey,2019-09-25 12:04:00
owl,2019-09-25 12:26:00
owl,2019-09-25 12:17:00
數據太多就不寫了其中
donkey12:04<第三次的水印12:11
屬於過期數據,不參與計算
Batch: 5 null
Batch: 6
|window                                    |word|count|
+------------------------------------------+----+-----+
|[2019-09-25 12:05:00, 2019-09-25 12:15:00]|owl |2    |
|[2019-09-25 12:05:00, 2019-09-25 12:15:00]|dog |3    |
|[2019-09-25 12:05:00, 2019-09-25 12:15:00]|cat |1    |
+------------------------------------------+----+-----+
此時這裏的watermark=12::26-10=12:16
batch6輸出的內容是第三次輸出數據後內存中<12:16的數據
12:00-12:10的已經被剔除了,所以此次剔除的是12:05-12:15中的數據
第四次輸入的數據 cat,2019-09-25 12:09:00 cat同理過期 batch7 :null 此時沒有輸出,因爲此時水印還是12:16,過期數據已經被刪除完了

 

這裏主要說明兩點

1、我第四次輸出結果是 owl 2 dog 3 cat 1 spark 結果是owl 2 dog 3 cat 2

出現差異的原因是,第三次輸入的數據cat,2019-09-25 12:15:00這個的開窗出現了差異根據源碼
   * The windows are calculated as below:
   * maxNumOverlapping <- ceil(windowDuration / slideDuration)
   * for (i <- 0 until maxNumOverlapping)
   *   windowId <- ceil((timestamp - startTime) / slideDuration)
   *   windowStart <- windowId * slideDuration + (i - maxNumOverlapping) * slideDuration + startTime
   *   windowEnd <- windowStart + windowDuration
   *   return windowStart, windowEnd
 windowStart =12:15-ceil(10/5)*5=12:05  所以應該劃分12:05-12:15  12:10-12:20這兩個窗口

spark官方應該是劃分爲12:10-12:20 12:15-12:25,個人猜測.

2、就是輸出時間的問題,我是在第三次和第四次輸入數據後,控制檯就打印了結果,spark官方圖上顯示的是第四次和第五次輸出的結果 先來段spark官方原話

The engine waits for “10 mins” for late date to be counted, then drops intermediate state of a window < watermark, and appends the final counts to the Result Table/sink. For example, the final counts of window 12:00 - 12:10 is appended to the Result Table only after the watermark is updated to 12:11.

翻譯:只有(只要)當水印=12:11的時候,之前的12:00 - 12:10 窗口數據就會被追加到結果表  即輸出到控制檯

是隻有還是隻要?第三次輸入數據後水印已經到達12:11,然後就把12:00-12:10的數據輸出了,同時這裏有兩個batch,個人感覺是隻要

當一個批次的數據的時候,structured-streaming 先獲得最大的水印,然後排除那些同批次的過期數據,然後會把一個批次的數據全部加載進內存計算出結果後,再剔除過期的內存數據

如果劃分到一條條數據,有個疑問,是此條最大水印的數據輸入後,立馬開始清理內存,還是在下一條數據來臨的時候再清理

歡迎大家探討,這個只是自己學習時的一點想法,不一定正確,歡迎批評,但別太嚴厲。。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章