你不得不知的Flink窗口

一、Windows詳解

如果你真的頭鐵看了上一篇的算子二,那麼沒有問題,看完這篇你應該會更加清晰的理解窗口,如果沒看也沒問題,我會適當的引入部分內容,但看完這篇後還是請打開算子二的窗口章節再去細品下相關算子!

1.1 圖解關係

在這裏插入圖片描述

1.2 窗口操作類型分類

對於窗口的操作分爲兩種,一種是keyedstrem,另一種是DataStream;他們的主要區別也僅僅在於建立窗口的時候一個爲.window(…),一個爲.windowAll(…)。經過keyBy的數據流將形成多組數據,下游算子的多個實例可以並行計算。而對於windowAll不對數據流進行分組,所有數據將發送到下游算子單個實例上。

1.2.1 窗口的大致骨架

// Keyed Window
stream
       .keyBy(...)               <-  按照一個Key進行分組
       .window(...)              <-  將數據流中的元素分配到相應的窗口中
      [.trigger(...)]            <-  指定觸發器Trigger(可選)
      [.evictor(...)]            <-  指定清除器Evictor(可選)
       .reduce/aggregate/process()      <-  窗口處理函數Window Function

// Non-Keyed Window
stream
       .windowAll(...)           <-  不分組,將數據流中的所有元素分配到相應的窗口中
      [.trigger(...)]            <-  指定觸發器Trigger(可選)
      [.evictor(...)]            <-  指定清除器Evictor(可選)
       .reduce/aggregate/process()      <-  窗口處理函數Window Function

1.2.2 窗口分類

窗口主要有兩種,一種基於時間(Time-based Window),一種基於數量(Count-based Window)。這裏主要討論的事基於時間的窗口TimeWindow。每個TimeWindow都有一個開始時間和結束時間,表示一個左閉右開的時間段。而時間窗口有可細分爲滾動窗口、滑動窗口和會話窗口,而每個窗口根據時間有可以分爲事件時間(Event Time)、攝取事件(Ingestion Time)、處理時間(Processing Time)。(基於數量的窗口根據事件到達窗口的先後順序管理窗口,到達窗口的先後順序和Event Time並不一致,因此Count-based Window的結果具有不確定性)

1.2.2.1 滾動窗口

滾動窗口下窗口之間不重疊,且窗口長度是固定的。我們可以用TumblingEventTimeWindows和TumblingProcessingTimeWindows創建一個基於Event Time或Processing Time的滾動時間窗口。窗口的長度可以用org.apache.flink.streaming.api.windowing.time.Time中的seconds、minutes、hours和days來設置。
在這裏插入圖片描述
舉例:

val input: DataStream[T] = ...

// 事件時間滾動窗口5秒
input
    .keyBy(...)
    .window(TumblingEventTimeWindows.of(Time.seconds(5)))
.<window function>(...)

// 處理時間滾動窗口5秒
input
    .keyBy(...)
    .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.<window function>(...)

// 事件時間滾動窗口1小時,時間偏移量30分鐘
input
    .keyBy(...)
    .window(TumblingEventTimeWindows.of(Time.hours(1), Time.minutes(30)))
    .<window function>(...)

注:有些代碼可能使用的是timeWindow而非window timeWindow是一種簡寫。當我們在執行環境設置了TimeCharacteristic.EventTime時,Flink對應調用TumblingEventTimeWindows;如果我們基於TimeCharacteristic.ProcessingTime,Flink使用TumblingProcessingTimeWindows

val env = StreamExecutionEnvironment.getExecutionEnvironment
// 從調用時刻開始給env創建的每一個stream追加時間特徵
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
val input: DataStream[T] = ...
input.keyBy(...).timeWindow(Time.seconds(1))

1.2.2.2 滑動窗口

滑動窗口以一個步長(Slide)不斷向前滑動,窗口的長度固定。使用時,我們要設置Slide和Size。Slide的大小決定了Flink以多大的頻率來創建新的窗口,Slide較小,窗口的個數會很多。Slide小於窗口的Size時,相鄰窗口會重疊,一個事件會被分配到多個窗口;Slide大於Size,有些事件可能被丟掉
在這裏插入圖片描述
例:窗口時間間隔可以使用Time.milliseconds(x), Time.seconds(x), Time.minutes(x)中的一種定義,跟前面介紹的一樣也可以用timeWindow代替

val input: DataStream[T] = ...
// sliding event-time windows
input
    .keyBy(...)
    .window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<window function>(...)
// sliding processing-time windows
input
    .keyBy(<...>)
    .window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<window function>(...)
// sliding processing-time windows offset by -8 hours
input
    .keyBy(<...>)
    .window(SlidingProcessingTimeWindows.of(Time.hours(12), Time.hours(1), Time.hours(-8)))
    .<window function>(...)

1.2.2.3 會話窗口

會話窗口根據Session gap切分不同的窗口,當一個窗口在大於Session gap的時間內沒有接收到新數據時,窗口將關閉。在這種模式下,窗口的長度是可變的,每個窗口的開始和結束時間並不是確定的。我們可以設置定長的Session gap,也可以使用SessionWindowTimeGapExtractor動態地確定Session gap的長度。
在這裏插入圖片描述
例:

val input: DataStream[T] = ...
// event-time session windows with static gap
input
    .keyBy(...)
    .window(EventTimeSessionWindows.withGap(Time.minutes(10)))
    .<window function>(...)
// event-time session windows with dynamic gap
input
    .keyBy(...)
    .window(EventTimeSessionWindows.withDynamicGap(new SessionWindowTimeGapExtractor[T] {
      override def extract(element: T): Long = {
        // determine and return session gap
      }
    }))
    .<window function>(...)
// processing-time session windows with static gap
input
    .keyBy(...)
    .window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))
    .<window function>(...)
// processing-time session windows with dynamic gap
input
    .keyBy(...)
    .window(DynamicProcessingTimeSessionWindows.withDynamicGap(new SessionWindowTimeGapExtractor[T] {
      override def extract(element: T): Long = {
        // determine and return session gap
      }
    }))
    .<window function>(...)
  • 靜態會話間隔可以使用Time.milliseconds(x), Time.seconds(x), Time.minutes(x)其中一種定義;
  • 動態會話間隔可以通過實現 SessionWindowTimeGapExtractor 接口;
  • 注意:由於session window會話窗口沒有固定的開始和結束時間,因此它們的計算加工方式與

tumbling window和sliding window不同。 在內部,session window 算子爲每個到達的記錄創建一個新窗口,如果它們彼此之間的距離比定義的間隙更接近,則將窗口合併在一起。 爲了可合併,session window 算子需要合併觸發器和合並窗口函數,例如ReduceFunction,AggregateFunction或ProcessWindowFunction(FoldFunction無法合併。)

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