《從0到1學習Flink》—— Flink 中幾種 Time 詳解
前言
Flink 在流程式中支援不同的 Time 概念,就比如有 Processing Time、Event Time 和 Ingestion Time。
下面我們一起來看看這幾個 Time:
Processing Time
Processing Time 是指事件被處理時機器的系統時間。
當流程式在 Processing Time 上執行時,所有基於時間的操作(如時間視窗)將使用當時機器的系統時間。每小時 Processing Time 視窗將包括在系統時鐘指示整個小時之間到達特定操作的所有事件。
例如,如果應用程式在上午 9:15 開始執行,則第一個每小時 Processing Time 視窗將包括在上午 9:15 到上午 10:00 之間處理的事件,下一個視窗將包括在上午 10:00 到 11:00 之間處理的事件。
Processing Time 是最簡單的 "Time" 概念,不需要流和機器之間的協調,它提供了最好的效能和最低的延遲。但是,在分散式和非同步的環境下,Processing Time 不能提供確定性,因為它容易受到事件到達系統的速度(例如從訊息佇列)、事件在系統內操作流動的速度以及中斷的影響。
Event Time
Event Time 是事件發生的時間,一般就是資料本身攜帶的時間。這個時間通常是在事件到達 Flink 之前就確定的,並且可以從每個事件中獲取到事件時間戳。在 Event Time 中,時間取決於資料,而跟其他沒什麼關係。Event Time 程式必須指定如何生成 Event Time 水印,這是表示 Event Time 進度的機制。
完美的說,無論事件什麼時候到達或者其怎麼排序,最後處理 Event Time 將產生完全一致和確定的結果。但是,除非事件按照已知順序(按照事件的時間)到達,否則處理 Event Time 時將會因為要等待一些無序事件而產生一些延遲。由於只能等待一段有限的時間,因此就難以保證處理 Event Time 將產生完全一致和確定的結果。
假設所有資料都已到達, Event Time 操作將按照預期執行,即使在處理無序事件、延遲事件、重新處理歷史資料時也會產生正確且一致的結果。 例如,每小時事件時間視窗將包含帶有落入該小時的事件時間戳的所有記錄,無論它們到達的順序如何。
請注意,有時當 Event Time 程式實時處理實時資料時,它們將使用一些 Processing Time 操作,以確保它們及時進行。
Ingestion Time
Ingestion Time 是事件進入 Flink 的時間。 在源操作處,每個事件將源的當前時間作為時間戳,並且基於時間的操作(如時間視窗)會利用這個時間戳。
Ingestion Time 在概念上位於 Event Time 和 Processing Time 之間。 與 Processing Time 相比,它稍微貴一些,但結果更可預測。因為 Ingestion Time 使用穩定的時間戳(在源處分配一次),所以對事件的不同視窗操作將引用相同的時間戳,而在 Processing Time 中,每個視窗操作符可以將事件分配給不同的視窗(基於機器系統時間和到達延遲)。
與 Event Time 相比,Ingestion Time 程式無法處理任何無序事件或延遲資料,但程式不必指定如何生成水印。
在 Flink 中,,Ingestion Time 與 Event Time 非常相似,但 Ingestion Time 具有自動分配時間戳和自動生成水印功能。
說了這麼多概念比較乾澀,下面直接看圖:
設定時間特性
Flink DataStream 程式的第一部分通常是設定基本時間特性。 該設定定義了資料流源的行為方式(例如:它們是否將分配時間戳),以及像 KeyedStream.timeWindow(Time.seconds(30))這樣的視窗操作應該使用上面哪種時間概念。
以下示例顯示了一個 Flink 程式,該程式在每小時時間視窗中聚合事件。
1final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
2
3env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime);
4
5// 其他
6// env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime);
7// env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
8
9DataStream<MyEvent> stream = env.addSource(new FlinkKafkaConsumer09<MyEvent>(topic, schema, props));
10
11stream
12 .keyBy( (event) -> event.getUser() )
13 .timeWindow(Time.hours(1))
14 .reduce( (a, b) -> a.add(b) )
15 .addSink(...);
Event Time 和 Watermarks
注意:Flink 實現了資料流模型中的許多技術。有關 Event Time 和 Watermarks 的詳細介紹,請檢視以下文章:
https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101
https://research.google.com/pubs/archive/43864.pdf
支援 Event Time 的流處理器需要一種方法來衡量 Event Time 的進度。 例如,當 Event Time 超過一小時結束時,需要通知構建每小時視窗的視窗操作符,以便操作員可以關閉正在進行的視窗。
Event Time 可以獨立於 Processing Time 進行。 例如,在一個程式中,操作員的當前 Event Time 可能略微落後於 Processing Time (考慮到接收事件的延遲),而兩者都以相同的速度進行。另一方面,另一個流程式可能只需要幾秒鐘的時間就可以處理完 Kafka Topic 中數週的 Event Time 資料。
Flink 中用於衡量 Event Time 進度的機制是 Watermarks。 Watermarks 作為資料流的一部分流動並帶有時間戳 t。 Watermark(t)宣告 Event Time 已到達該流中的時間 t,這意味著流中不應再有具有時間戳 t'<= t 的元素(即時間戳大於或等於水印的事件)
下圖顯示了帶有(邏輯)時間戳和內聯水印的事件流。在本例中,事件是按順序排列的(相對於它們的時間戳),這意味著水印只是流中的週期性標記。
Watermark 對於無序流是至關重要的,如下所示,其中事件不按時間戳排序。通常,Watermark 是一種宣告,通過流中的該點,到達某個時間戳的所有事件都應該到達。一旦水印到達操作員,操作員就可以將其內部事件時間提前到水印的值。
平行流中的水印
水印是在源函式處生成的,或直接在源函式之後生成的。源函式的每個並行子任務通常獨立生成其水印。這些水印定義了特定並行源處的事件時間。
當水印通過流程式時,它們會提前到達操作人員處的事件時間。當一個操作符提前它的事件時間時,它為它的後續操作符在下游生成一個新的水印。
一些操作員消耗多個輸入流; 例如,一個 union,或者跟隨 keyBy(…)或 partition(…)函式的運算子。 這樣的操作員當前事件時間是其輸入流的事件時間的最小值。 由於其輸入流更新其事件時間,因此操作員也是如此。
下圖顯示了流經並行流的事件和水印的示例,以及跟蹤事件時間的運算子。
原文釋出時間為:2019-1-3
本文作者: zhisheng
本文來自雲棲社群合作伙伴“ zhisheng”,瞭解相關資訊可以關注“zhisheng_blog”微信公眾號