Flink學習筆記之流處理基礎

流處理基礎

流編程簡介

數據流圖(DataFlow Graph)

數據流程序描述了數據如何在算子之間流動。數據流程序通常表示爲有向圖,其中節點稱爲算子,用來表示計算。算子是數據流程序的基本功能單元。
DataFlow Graph
如上圖所示,數據流圖被稱爲邏輯流圖。爲了執行一個數據流程序,Flink會將邏輯流圖轉換爲物理數據流圖。

數據並行和任務並行

我們可以以不同的方式利用數據流圖中的並行性:

  • 我們可以對輸入數據進行分區,並在數據的子集上並行執行具有相同算子的任務並行。即數據並行,數據並行是有限度的,因爲他允許處理大量數據,並將計算分散到不通透計算節點上。
  • 我們可以將不同算子在相同或不同的數據上並行執行。即任務並行,可以更好地利用資源。

數據交換策略

數據交換策略定義了在物理執行流圖中如何給數據分配給任務。數據交換策略可以由執行引擎自動選擇,具體取決於算子的語義或我們明確指定的語義。
數據交換側略

  • 前向策略將數據從一個任務發送到接收任務。如果兩個任務都位於同一臺物理計算機上(這通常由任務調度器確保),這種交換策略可以避免網絡通信。
  • 廣播策略將所有數據發送到算子的所有的並行任務上面去。因爲這種策略會複製數據和涉及網絡通信,所以代價相當昂貴。
  • 基於鍵控的策略通過Key值(鍵)對數據進行分區保證具有相同Key的數據將由同一任務處理。
  • 隨機策略統一將數據分配到算子的任務中去,以便均勻地將負載分配到不同的計算任務。

並行處理流數據

延遲和吞吐量

流處理程序是連續運行的,輸入可能是無界的,所以數據流處理中沒有總執行時間的概念。相反,流處理程序必須儘可能快的提供輸入數據的計算結果。因此,引入延遲和吞吐量來表徵流處理的性能要求。

延遲

延遲表示處理事件所需要的時間,是接收事件和看到輸出中處理此事件的效果之間的時間間隔。

吞吐量

吞吐量是衡量系統處理能力的指標,也就是處理速率。
吞吐量以每個時間爲單位系統所能處理的事件數量或操作數量來衡量。阿紫流式系統中,我們希望確保我們的系統可以處理最大的預期事件到達的速率,即我們主要關心的在於確定的峯值吞吐量是多少,當系統處於最大負載性能怎樣。

爲了更好地理解峯值吞吐量的概念,讓我們考慮一個流處理 程序沒有收到任何輸入的數據,因此沒有消耗任何系統資源。當第一個事件進來時,它會儘可能以最小延遲立即處理。例如,如果你是第一個出現在咖啡店的顧客,在早上開門後,你將立即獲得服務。理想情況下,您希望此延遲保持不變 ,並且獨立於傳入事件的速率。但是,一旦我們達到使系統資源被完全使用的事件傳入速率,我們將不得不開始緩衝事件。在咖啡店裏 ,午餐後會看到這種情況發生。許多人出現在同一時間,必須排隊等候。在此刻,咖啡店系統已達到其峯值吞吐量,進一步增加 事件傳入的速率只會導致更糟糕的延遲。如果系統繼續以可以處理的速率接收數據,緩衝區可能變爲不可用,數據可能會丟失。這種情況是衆所周知的 作爲背壓,有不同的策略來處理它。

延遲和吞吐量的對比

此時,應該清楚延遲和吞吐量不是獨立指標。如果事件需要在處理流水線中待上很長時間,我們不能輕易確保高吞吐量。同樣,如果系統容量很小,事件將被緩衝,而且必須等待才能得到處理。

例子來闡明一下延遲和吞吐量如何相互影響。首先,應該清楚存在沒有負載時的最佳延遲。也就是說,如果你是咖啡店的唯一客戶,會很快得到咖啡。然而,在繁忙時期,客戶將不得不排隊等待,並且會有延遲增加。另一個影響延遲和吞吐量的因素是處理事件所花費的時間或爲每個客戶提供服務所花費的時間。想象一下,期間聖誕節假期,咖啡師不得不爲每杯咖啡畫聖誕老人。這意味着準備一杯咖啡需要的時間會增加,導致每個人花費 更多的時間在等待咖啡師畫聖誕老人,從而降低整體吞吐量。

那麼,你可以同時獲得低延遲和高吞吐量嗎?或者這是一個無望的努力?我們可以降低得到咖啡的延遲 ,方法是:聘請一位更熟練的咖啡師來準備咖啡。在高負載時,這種變化也會增加吞吐量,因爲會在相同的時間內爲更多的客戶提供服務。 實現相同結果的另一種方法是僱用第二個咖啡師來利用並行性。這裏的主要想法是降低延遲來增加吞吐量。當然,如果系統可以更快的執行操作,它可以在相同的時間內執行更多操作。 事實上,在流中利用並行性時也會發生這種情況。通過並行處理多個流,在同時處理更多事件的同時降低延遲。

數據流上的操作

數據攝入和數據吞吐量

數據攝取和數據出口操作允許流處理程序與外部系統通信。數據攝取是操作從外部源獲取原始數據並將其轉換爲其他格式(ETL)。實現數據提取邏輯的運算符被稱爲數據源。

轉換算子


轉換算子是單遍處理算子,碰到一個事件處理一個事件。這些操作在使用後會消費一個事件,然後對事件數據做一些轉換,產生一個新的輸出流。轉換邏輯可以集成在操作符中或有UDF函數提供。自然開發人員可以自定義計算邏輯。

滾動聚合

滾動聚合是一種聚合方式,例如:sum , minimun, maximum ,爲每個輸入事件不斷更新。聚合操作是有狀態的,並將當前狀態與傳入事件一起計算以產生更新的聚合值。

窗口操作符

轉換和滾動聚合一次處理一個事件產生輸出事件並可能更新狀態。但是,有些操作必須收集並緩衝數據以計算其結果。例如考慮不同流之間的鏈接或整體聚合這樣的操作。
窗口還可以在語義上實現關於流的比較複雜的查詢。我們看到額滾動聚合的方式,以聚合值編碼·整個流的歷史數據來爲每個事件提供低延遲的結果,但如果我們只是對最近的數據感興趣會怎樣?

窗口操作不斷從無限事件流中創建有限的事件集,好讓我們執行有限集的計算。通常會基於數據屬性或基於時間的窗口來分配事件。 要正確定義窗口運算符語義,我們需要確定如何給窗口分配事件以及對窗口中的元素進行求值的頻率是什麼樣的。 窗口的行爲由一組策略定義。窗口策略決定何時創建新的窗口以及要分配的事件屬於哪個窗口,以及何時對窗口中的元素進行求值。 而窗口的求值基於觸發條件。一旦觸發條件得到滿足,窗口的內容將會被髮送到求值函數,求值函數會將計算邏輯應用於窗口中的元素。 求值函數可以是sum或minimal或自定義的聚合函數。 求值策略可以根據時間或者數據屬性計算(例如,在過去五秒內收到的事件或者最近的一百個事件等等)。 接下來,我們描述常見窗口類型的語義。

  • 滾動窗口是將事件分配到固定大小的不重疊的窗口中。當通過窗口的結尾是,全部時間被傳送到求值函數進行處理。基於計數的滾動窗口定義了在除法求值之前需要收集多少事件。如圖所示:一個基於計數的翻滾窗口,每四個元素一個窗口,基於時間的滾動窗口定義一個時間間隔,包含在此時間間隔內的事件.
    滾動窗口
    下圖顯示了基於時間的滾動時間的滾動窗口,將事件手機到窗口中每10分鐘觸發一次計算。
    滾動時間窗
  • 滑動窗口事件將分配到固定大小的重疊的窗口中去。因此,事件可能屬於多個桶。我們通過提供窗口的長度和滑動距離來定義滑動窗口。滑動距離定義了創建新窗口的間隔,基於滑動計數的窗口。如下圖所示:長度爲4個事件,3個爲滑動距離
  • 會話窗口再常見的真實場景中很有用。考慮一個分析在線用戶行爲的應用程序,在程序中,我們想把源自同一時期的用戶活動或會話事件分組在一起。會話有一系列相鄰時間發生的事件組成,接下來有一段時間沒有活動。例如:用戶在APP上瀏覽一系列的新聞,然後關閉APP,那麼瀏覽新聞這段時間的瀏覽事件就是一個會話。
  • 會話窗口事先沒有定義窗口的長度,而是取決於數據的實際情況。我們將同一會話中的事件分配到同一個窗口中去。而不同會話可能窗口長度不一樣。會話窗口會定義一個間隙來區分不同的會話。**間隙:**用戶一段時間內部活動,就認爲用戶的會話結束了。下圖爲一個會話窗口:
  • 到目前爲止,所有窗口類型都是在整條流上去做窗口操作。但實際上可能要將一條流分流成多個邏輯流並定義並行窗口。如圖所示:一個基於計數的長度爲2的並行滾動窗口,根據時間顏色分流。

時間語義

在流處理中,窗口操作與兩個主要概念密切相關:時間語義和狀態管理。時間也許是流處理最重要的方面。即使低延遲是流處理的一個有吸引力的特性,它的真正價值不僅僅是快速分析。真實世界的系統,網絡和通信渠道遠非完美,流數據經常被推遲或無序(亂序)到達。理解如何在這種條件下提供準確和確定的結果是至關重要的。 更重要的是,流處理程序可以按原樣處理事件製作的也應該能夠處理相同的歷史事件方式,從而實現離線分析甚至時間旅行分析。

處理時間

處理時間是處理流的應用程序的機器的本地時鐘的時間(牆上時鐘)。處理時間的窗口包含了一個時間段內來到機器的所有事件。

事件時間

事件事件是流中的事件實際發生的時間。事件時間基於流中的事件所包含的時間戳。通常情況下,在事件進入流處理程序前,事件數據就已經包含了時間戳。
事件時間使得計算結果的過程不需要依賴處理數據的速度。基於事件時間的操作是可以預測的,而計算結果也是確定的。無論流處理程序處理流數據的速度快或是慢,無論事件到達流處理程序的速度快或是慢,事件時間窗口的計算結果都是一樣的。

水位線(Watermarks)

在我們對事件時間窗口的討論中,我們忽略了一個很重要的方面:我們應該怎樣去決定何時觸發事件時間窗口的計算?也就是說,在我們可以確定一個時間點之前的所有事件都已經到達之前,我們需要等待多久?我們如何知道事件是遲到的?在分佈式系統無法準確預測行爲的現實條件下,以及外部組件所引發的事件的延遲,以上問題並沒有準確的答案。在本小節中,我們將會看到如何使用水位線來設置事件時間窗口的行爲。

水位線是全局進度的度量標準。系統可以確信在一個時間點之後,不會有早於這個時間點發生的事件到來了。本質上,水位線提供了一個邏輯時鐘,這個邏輯時鐘告訴系統當前的事件時間。當一個運算符接收到含有時間T的水位線時,這個運算符會認爲早於時間T的發生的事件已經全部都到達了。對於事件時間窗口和亂序事件的處理,水位線非常重要。運算符一旦接收到水位線,運算符會認爲一段時間內發生的所有事件都已經觀察到,可以觸發針對這段時間內所有事件的計算了。

水位線提供了一種結果可信度和延時之間的妥協。激進的水位線設置可以保證低延遲,但結果的準確性不夠。在這種情況下,遲到的事件有可能晚於水位線到達,我們需要編寫一些代碼來處理遲到事件。另一方面,如果水位線設置的過於寬鬆,計算的結果準確性會很高,但可能會增加流處理程序不必要的延時。

狀態和持久化模型

在數據處理中,狀態時普遍存在的。任何稍微複雜一點的計算,都會涉及到狀態。爲了產生計算結果,一個函數在一段時間內的一定數量的事件上來累加狀態,例如聚合計算或者模式匹配。有狀態的運算符試用輸入的事件以及內部保存的狀態來計算得到輸出。

當我們考慮一下使用批處理系統來分析一個無界數據集時,會發現狀態的重要性顯而易見。在現代流處理器興起之前,處理無界數據集的一個通常做法是將輸入的事件攢成微批,然後交由批處理器來處理。當一個任務結束時,計算結果將被持久化,而所有的運算符狀態就丟失了。一旦一個任務在計算下一個微批次的數據時,這個任務是無法訪問上一個任務的狀態的(都丟掉了)。這個問題通常使用將狀態代理到外部系統(例如數據庫)的方法來解決。相反,在一個連續不間斷運行的流處理任務中,事件的狀態是一直存在的,我們可以將狀態暴露出來作爲編程模型中的一等公民。當然,我們的確可以使用外部系統來管理流的狀態,即使這個解決方案會帶來額外的延遲。

由於流處理運算符默認處理的是無界數據流。所以我們必須要注意不要讓內部狀態無限的增長。爲了限制狀態的大小,運算符通常情況下會保存一些之前所觀察到的事件流的總結或者概要。這個總結可能是一個計數值,一個累加和,或者事件流的採樣,窗口的緩存操作,或者是一個自定義的數據結構,這個數據結構用來保存數據流中感興趣的一些特性。

狀態管理
系統需要高效的管理狀態,並保證針對狀態的併發更新,不會產生競爭條件(race condition)
狀態分區
並行會帶來複雜性,因爲計算結果同事取決於已經保存的狀態和輸入的事件流。幸運的是,絕大數情況下,我們可以使用key來對狀態進行分區,然後獨立管理每一個分區。
狀態恢復
需要保證有狀態的運算符可以恢復,即便出現任務失敗,計算也是正確的。

任務失敗

什麼是任務失敗
對於流中的每一個事件,一個處理任務分爲以下步驟:

  • 接收事件,並將事件存儲在本地緩存中;
  • 可能會更新內部狀態;
  • 產生輸出記錄
    在批處理中,我們可以方便的重新計算,因此所有的問題都不是問題。但是在流的世界中,處理失敗不是一件小事。因此需要保證流系統在失敗的情況下需要保證結果的準確性。
    結果的保證
    要注意保證應用程序狀態的一致性並不是保證應用程序的輸出結果的一致性。一旦輸出結果被持久化,結果的準確性就很難保證了。除非持久化系統支持事務。
    AT-MOST-ONCE
    當任務發生時,最簡單的做法就是什麼都不幹,既不恢復丟失的狀態,也不重播丟失的事件。AT-MOST-ONCE語義就是最多處理一次事件。
    AT-LEAST-ONCE
    我們希望不丟失事件,這種類型的保證便是我們的 AT-LEAST-ONCE語義是所有的事件都得到了處理,而且一些事件還可能被處理多次。如果結果的正確性僅僅依賴於數據的完整性,那麼重複是可以接受的。
    爲了保證在AT-LEAST-ONCE語義下,計算結果也能正確。我們還要有另一套系統來從數據源或者緩存中重新播放數據。
  • 一種方法是持久化的事件日誌文件系統會將所有的事件寫入到持久化存儲中,多以任務失敗,這些數據是可以重播的。
  • 還有一種方法可以獲得同等的效果,就是使用結果承認機制。這種方法將會把每一條數據都保存在緩存中,直到數據的處理等到所有的任務的承認。一旦得到所有任務的承認,數據將被丟棄。
    EXACTLY-ONCE
    恰好處理是最嚴格的的保證,也是最難實現的。恰好處理一次語義不僅僅意味着沒有事件丟失,還意味着針對每一個數據,內部狀態僅僅更新一次。
    提供恰好處理一次語義的保證必須有至少處理一次語義的保證才行,同時還需要數據重放機制。另外,流處理器還需要保證內部狀態的一致性。也就是說,在故障恢復以後,流處理器應該知道一個事件有沒有在狀態中更新。事務更新是達到這個目標的一種方法,但可能引入很大的性能問題。Flink使用了一種輕量級快照機制來保證恰好處理一次語義
    端到端恰好處理一次
    目前我們看到的一致性保證都是由流處理器實現的,也就是說都是在Flink流處理器內部保證的。而在真實世界中,流處理應用除了流處理器以外還包含了數據源(例如Kafka)和持久化系統。端到端的一致性保證意味着結果的正確性貫穿了整個流處理應用的始終。每一個組件都保證了它自己的一致性。而整個端到端的一致性級別取決於所有組件中一致性最弱的組件。要注意的是,我們可以通過弱一致性來實現更強的一致性語義。例如,當任務的操作具有冪等性時,比如流的最大值或者最小值的計算。在這種場景下,我們可以通過最少處理一次這樣的一致性來實現恰好處理一次這樣的最高級別的一致性。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章