《Streaming 102》: Beam模型

寫在前面

Akidau對Streaming System提出了五個基本概念,其中EventTime/ProcessTime和Window在《Streaming 101》已經介紹過了。如果不熟悉EventTime和ProcessTime的區別,或者不熟悉無界數據的Window劃分,請停止閱讀本文,從Streaming 101開始。

除了上面兩個,Streaming System還有三個重要的概念,這篇Streaming 102主要分析的就是剩下的這三個:

  • Trigger 觸發器 何時窗口結果被持久存儲到外部;做個比喻,觸發器類似相機的快門,它定義了什麼時候讓計算結果留下快照。
  • Watermark 水印 針對事件時間的輸入完整性概念;
  • Accumulation 累加器 對同一個窗口的多組計算結果,它們是如何累加的;遲到的數據如何影響之前的結果;

此外,爲了描述數據和操作,我們需要借用Beam中的PCollections和PTransforms的概念,這些概念都是抽象的,引入它們僅用來解釋概念,和流式系統的具體實現無關。

  • PCollections 數據集、並行轉換操作的執行對象
  • PTransforms 對數據集的操作,例如分組group/聚集aggragate等

一、批式: What and Where

從簡單的例子開始,有一個數據集UserScores,它包含三個字段:姓名Name,隊伍Team,分數Score,此外還包括事件時間EventTime、處理時間ProcTime。全部數據見下表:

姓名 隊伍 得分 事件時間 處理時間
Julie TeamX 5 12:00:26 12:05:19
Frank TeamX 9 12:01:26 12:08:19
Ed TeamX 7 12:02:26 12:05:39
Julie TeamX 8 12:03:06 12:07:06
Amy TeamX 3 12:03:39 12:06:13
Fred TeamX 4 12:04:19 12:06:39
Naomi TeamX 3 12:06:39 12:07:19
Becky TeamX 8 12:07:26 12:08:39
Naomi TeamX 1 12:07:46 12:09:00

二、流式: When and How

1. When Trigger

這一節講什麼是Trigger。“在處理時間的什麼時刻,處理結果被物化?”, Trigger實際上回答了這個問題。在處理結果物化的時機選擇上有非常多的可能,如果做一個分類,可以分爲基於事件時間的Trigger、基於處理時間的Trigger、基於數據的Trigger,例如:

  • AfterWatermark 一類基於事件時間的trigger,當watermark超過了window的最後範圍,AfterWatermark trigger會釋放窗口的內容。Watermark是全局進度的度量,表示着在一個特定時刻輸入數據的完整性。事件時間由數據攜帶的時間戳決定。
 // Create a bill at the end of the month.
  AfterWatermark.pastEndOfWindow()
      // During the month, get near real-time estimates.
      .withEarlyFirings(
          AfterProcessingTime
              .pastFirstElementInPane()
              .plusDuration(Duration.standardMinutes(1))
      // Fire on any late data so the bill can be corrected.
      .withLateFirings(AfterPane.elementCountAtLeast(1))
  • AfterProcessingTime 一類基於處理時間的trigger,例如AfterProcessingTime.pastFirstElementInPane在收到數據的特定處理時間後會釋放窗口的內容。處理時間由系統時鐘決定。

  • AfterPane 一類基於數據的trigger,例如AfterPane.elementCountAtLeast(),這個trigger在當前pane收集到至少N個元素後釋放窗口的內容。這種trigger可以讓window釋放早期結果,當你只使用一個全局窗口做流式計算的時候特別有用。需要額外注意,如果你指定了.elementCountAtLeast(50)但是隻收到了32個元素,那沒有辦法觸發執行,這32個元素就永遠閒着了。

  • 複合trigger 上個例子中不足以觸發數據量trigger的32個元素,如果這部分數據很重要,你可以組合trigger,指定複合trigger,例如“每收到50條數據觸發一次,或者每1秒觸發一次”。

上面提到的分類參考自Beam API,在Streaming 102原文中提到的分類方式比上述更晦澀一些,按照Repeated update triggerCompleteness trigger進行分類。簡單說,特定時間或者特定數據條數觸發的trigger,叫做repeated update trigger;而特定window的數據在watermark範圍內完整後觸發的trigger,叫做completeness trigger。這也就引出了When的第二個重要概念,即watermark。

2. When Watermark

回到上一節開頭的問題 “在處理時間的什麼時刻,處理結果被物化?” 你會回答是在trigger定義的時刻,結果被系統物化。回答正確,但並不夠完整。Watermark是在事件時間領域,針對輸入完整性的臨時表示。回顧Streaming 101的這張圖,圖中描述了事件時間和處理時間的gap,即時間傾斜問題。

在這裏插入圖片描述

常見的Window

  • Fixed Time Windows 固定時間窗口
    在這裏插入圖片描述

  • Sliding Time Windows 滑動時間窗口
    在這裏插入圖片描述

  • Per-Session Windows 會話窗口
    在這裏插入圖片描述

  • Single Global Window 單個全局窗口

  • Calendar-based Windows 基於日曆的窗口


有一些transforms, 例如GroupByKey和Combine,根據key把元素分組,相同key的元素屬於相同的組。

  • 完美水印 理想情況的水印,但無法達到,你永遠不知道數據在什麼時候到齊
  • 啓發式水印 實際能做到的水印,通常根據經驗人爲指定

兩類水印的計算過程如下圖,左邊是完美水印,右邊的是啓發式水印:
左邊的數據"9"是一條遲到數據,它的事件時間是[12:01, 12:02),但是在[12:08,12:09)到達流式處理系統,
在完美水印情況下,遲到的數據“9”也會被統計到,每個窗口的水印見圖4的左半部分。
在啓發式水印情況下,遲到的數據“9”會被劃分在窗口之外。

對左圖來說,[12:02, 12:04), [12:04, 12:06), [12:06, 12:08) 這三個窗口的物化時刻(見圖3,藍色表示已物化)太慢了,因爲這三個窗口的數據已經在12:08之前到齊。對右圖來說,[12:00,12:02)這個窗口的物化時刻(見圖1,藍色表示已物化)太快了,因爲有一條遲到的數據“9”,太快的物化導致[12:00, 12:02)這個窗口的統計結果是5,而不是正確的5+9=14。

有些情況太慢了,有些情況又太快了。 永遠不存在完美的水印。只通過啓發式水印來物化輸出不足以得到完全準確的結果。
在這裏插入圖片描述
▲ 圖 1: 處理時間12:06

在這裏插入圖片描述
▲ 圖 2: 處理時間12:08

在這裏插入圖片描述
▲ 圖 3: 處理時間12:10

在這裏插入圖片描述
▲ 圖 4: 不再修改的水印

3. When:Composite Triggers

前面提到了Repeated update triggerCompleteness trigger,在很多情況下,只用兩者中的一種是不夠的,需要組合使用。在Beam中提供了一種標準watermark trigger的擴展,支持在水印的任意一側更新trigger。在Beam中,這些擴展稱作early/on-time/late trigger。Beam包括了下述複合trigger:

  • AfterWatermark.pastEndOfWindow 在窗口左側更新trigger選擇.withEarlyFirings;在窗口右側更新trigger選擇.withLateFirings;
  • Repeatedly.forever 每次trigger條件達到,就引發一個窗口釋放結果;
  • AfterEach.inOrder 把多個trigger組合到一個序列中,每次序列中的trigger釋放一個窗口,trigger條件切換到下一個trigger;
  • AfterFirst 接收多個trigger,當任意一個trigger條件達成時,窗口釋放結果;
  • AfterAll 接收多個trigger,當全部trigger條件達成時,窗口釋放結果;
  • orFinally 在任何trigger後面添加,觸發一次釋放結果,然後再也不釋放;

在Beam的代碼中,上述trigger的樣例代碼如下

PCollection<KV<Team, Integer>> totals = input
	.apply(Window.into(FixedWindows.of(TWO_MINUTES))
			.triggering(AfterWatermark()
					.withEarlyFirings(AlignedDelay(ONE_MINUTE))
					.withLateFirings(AfterCount(1))))
	.apply(Sum.integersPerKey());

使用複合trigger之後,處理過程變成圖5-圖8所示:
在這裏插入圖片描述
▲ 圖 5: 處理時間12:06,兩個窗口存在數據;Early Firings,即使window數據沒完整也觸發一次計算,避免“太慢”的問題;

在這裏插入圖片描述
▲ 圖 6:

在這裏插入圖片描述
▲ 圖 7:
在這裏插入圖片描述
▲ 圖 8: Late Firings, 當啓發式watermark太快時,一旦出現窗口外的數據,立刻觸發一次修正

  • Early Trigger 在watermark(圖8中,左圖綠色虛線,右圖綠色實線)到達window的結尾之前,重複觸發,表示雖然window還沒到最後時刻,但數據要觸發一次計算,窗口內的數據叫Early Pane。Early Firings,即使window數據沒完整也觸發一次計算,一定程度避免了“太慢”的問題;
  • On-Time Trigger 在watermark剛好到達window的結尾處時,觸發一次,窗口內的數據叫做On-Time Pane。這種情況很少見。
  • Late Trigger 在watermark; Late Firings, 當啓發式watermark太快時,一旦出現窗口外的數據,立刻觸發一次修正;一定程度解決了“太快”的問題。

無論是那種watermark, 隨着時間推移,window的範圍都得到了一定程度的修正,同時平衡了數據的及時性和準確性,這就是引入複合Trigger的價值。

4. When: Allowed Lateness

什麼是Allowed Lateness呢?我們直接看圖說話(建議放大到全屏查看)。
我們知道無界數據按事件時間被劃分成一個個窗口(圖中橫座標的區間),在達到Trigger條件時觸發計算操作。當處理時間到達一定階段後 ,比較早的窗口就再也不會看到數據,每個窗口處理時間的上限叫做watermark(圖中綠色實線)。當watermark通過窗口終點後(圖中綠色實線穿過橫座標的窗口區間),我們已經可以關閉窗口的狀態,清空這部分存儲空間了。在啓發式watermark中,如果我們讓窗口的右邊界允許再向右擴展,留一部分時間等待遲到數據,那這個向右繼續擴展的窗口邊界範圍就叫做allowed lateness或者lateness horizon。 顯然,lateness horizon是在事件時間的範疇定義的。允許的遲到時間越長,窗口狀態數據的保留時間越長,對流式系統的存儲空間要求也就越高。也就是說,lateness horizon使用更多的窗口狀態存儲空間來換取流式統計結果的準確性

例如圖9中的左邊,[12:00, 12:02)這個窗口被允許1分鐘的lateness horizon, 保留到12:03; [12:02, 12:04)這個窗口被允許1分鐘的lateness horizon, 保留到12:05;
在這裏插入圖片描述
▲ 圖 9: 允許遲到的時間
在這裏插入圖片描述
▲ 圖 10: 允許遲到的時間

Lateness horizon用Beam API withAllowedLateness來描述,如下述代碼所示:

PCollection<KV<Team, Integer>> totals = input
  .apply(Window.into(FixedWindows.of(TWO_MINUTES))
               .triggering(
                 AfterWatermark()
                   .withEarlyFirings(AlignedDelay(ONE_MINUTE))
                   .withLateFirings(AfterCount(1)))
               .withAllowedLateness(ONE_MINUTE))
 .apply(Sum.integersPerKey());

5. How: Accumulation

如果一個窗口的多次Trigger產生了多個Pane,那麼這個窗口的最終結果如何在多個pane的結果中產生?這就是Accumulation要定義的內容。有三種Accumulation模式:Discarding、Accumulating、Accumulating & Retracting,它們的處理過程見下表:

丟棄 累積 累積、撤回
Pane 1: inputs=[3] 3 3 3
Pane 2: inputs=[8,1] 9 12 12, -3
Final Pane 9 12 12
Sum of all Panes 12 15 12
  • Discarding 丟棄 當每個窗口釋放的是某種中間形式的數據,下游系統(而不是流式系統)會真正執行計算邏輯,這種情況適合丟棄上次的結果;舉個例子,計算窗口內的數字之和,上次的結果count=3,新進入的數據是9,在丟棄模式下,最終結果是9;
  • Accumulating 累積 計算窗口內的數字之和,上次的結果count=3,新進入的數據是9,在累積模式下,最終結果是3+9=12;
  • Accumulating and retracting 累積、撤回 繼續上面的例子,當結果更新爲12後,同時發出一個撤回操作-3,表示之前的3是個已經失效的結果,需要撤回;當下遊消費者對數據按另外一個維度做重新分組時,之前的結果可能被分配到新的組,因此只覆蓋舊結果還不夠,需要“撤回操作”刪除舊值;當動態劃分窗口時,新數據的到來會引起相關窗口的重新劃分。

上述累加模式的存儲成本和計算成本是遞增的。

參考

[1] Apache Beam官方文檔
[2] Streaming 102
[3] Streaming Systems

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