天天看點

優化 Apache Flink 應用程式的 3 個技巧(譯文)

作者:閃念基因
優化 Apache Flink 應用程式的 3 個技巧(譯文)

作者:Kevin Lam 和 Rafael Aguiar

在 Shopify,我們采用Apache Flink作為标準的有狀态流引擎,為各種用例提供​支援。今年早些時候,我們分享了優化大型有狀态 Flink 應用程式的技巧。下面我們将向您介紹另外 3 個最佳實踐。

1.設定正确的平行度

Flink 應用程式由多個任務組成,包括轉換(運算符)、資料源和接收器。這些任務被拆分成幾個并行執行個體用于執行和資料處理。

并行性是指任務的并行執行個體,是一種使您能夠擴充或擴充的機制。它是影響應用程式性能的主要因素之一。增加并行性允許應用程式利用更多任務槽,這可以提高整體吞吐量和性能。

可以通過幾種不同的方式配置應用程式并行性,包括:

  • 操作員級别
  • 執行環境級别
  • 用戶端級别
  • 系統級

配置選擇實際上取決于您的 Flink 應用程式的細節。例如,如果您的應用程式中的某些運算符已知是瓶頸,您可能隻想增加該瓶頸的并行度。

我們建議從單個執行環境級别的并行度值開始,并在需要時增加它。這是一個很好的起點,因為任務槽共享可以更好地利用資源。當 I/O 密集型子任務阻塞時,非 I/O 子任務可以使用任務管理器資源。

識别并行性時要遵循的一個好的規則是:

任務管理器的數量乘以每個任務管理器中的任務槽數必須等于(或略高于)最高并行度值

例如,當使用 100 的并行度(定義為預設執行環境級别或特定操作員級别)時,您将需要運作 25 個任務管理器,假設每個任務管理器有四個槽:25 x 4 = 100 。

2.避免水槽瓶頸

資料管道通常有一個或多個資料接收器(目的地如 Bigtable、Apache Kafka 等),這有時會成為 Flink 應用程式的瓶頸。例如,如果您的目标 Bigtable 執行個體具有高 CPU 使用率,則可能會由于 Flink 無法跟上寫入流量而開始影響您的 Flink 應用程式。您可能看不到任何異常,但一直到源的吞吐量都在下降。您還會在Flink UI中看到背壓。

當接收器成為瓶頸時,背壓将傳播到其所有上遊依賴項,這可能是您的整個管道。您想確定您的水槽永遠不會成為瓶頸!

在可以犧牲一點延遲的情況下,通過第一批寫入接收器以支援更高的吞吐量來解決瓶頸是很有用的。批量寫入請求是将多個事件收集為一個包并一次性送出給接收器的過程,而不是一次送出一個事件。批量寫入通常會導緻更好的壓縮、更低的網絡使用率和更小的 CPU 對接收器的影響。有關示例,請參見 Kafka 的batch.size屬性和 Bigtable 的批量突變。

您還需要檢查并修複任何資料偏差。在同一個 Bigtable 示例中,您可能有嚴重傾斜的鍵,這将影響 Bigtable 的一些最熱節點。Flink 使用鍵控流來擴充到節點。該概念涉及根據特定鍵對流的事件進行分區。Flink 然後在不同的節點上處理不同的分區。

KeyBy經常用于重新鍵入 aDataStream 以執行聚合或連接配接。它非常易于使用,但如果選擇的密鑰分布不當,可能會導緻很多問題。例如,在 Shopify,如果我們要選擇一個商店 ID 作為我們的鍵,那将是不理想的。店鋪号是我們平台上單個商戶店鋪的辨別。不同的 shop 流量差别很大,這意味着一些 Flink 任務管理器會忙于處理資料,而其他人則閑置。這很容易導緻記憶體不足異常和其他故障。低基數 ID(< 100)也有問題,因為很難在任務管理器之間正确配置設定它們。

但是,如果您絕對需要使用不太理想的密鑰怎麼辦?那麼,您可以應用分桶技術:

  • 選擇一個最大數(從小于或等于運算符并行度的數開始)
  • 随機生成一個介于 0 和最大數之間的值
  • 在 keyBy 之前将其附加到您的密鑰

通過應用分桶技術,您的處理邏輯可以更好地分布(達到每個鍵的最大附加分桶數)。但是,您需要想出一種方法來最終組合結果。例如,如果在處理完所有存儲桶後發現資料量顯着減少,則可以使用原始“不太理想”的密鑰對流進行 keyBy,而不會産生有問題的資料傾斜。如果您的查詢引擎支援,另一種方法是在查詢時合并您的結果。

3.用于 HybridSource合并異構源

假設您需要按照某種順序将多個異構資料源抽象為一個。例如,在 Shopify,我們大量的 Flink 應用程式讀取和寫入 Kafka。為了節省與存儲相關的成本,我們對所有 Kafka 主題實施按主題保留政策。這意味着經過一段時間後,資料将過期并從 Kafka 主題中删除。由于使用者可能在過期後仍然關心這些資料,是以我們支援配置 Kafka 主題進行歸檔。當一個主題被歸檔時,該主題的所有 Kafka 資料都被複制到一個雲對象存儲中進行長期存儲。這樣可以確定它在保留期結束時不會丢失。

現在,如果我們需要我們的 Flink 應用程式讀取與配置為存檔的主題相關的所有資料,我們該怎麼辦?好吧,我們可以建立兩個源——一個源用于從雲存儲檔案中讀取,一個源用于讀取實時 Kafka 主題。但這會造成複雜性。通過這樣做,我們的應用程式将同時從兩個不同的來源讀取事件時間中的兩個點。最重要的是,如果我們關心按順序處理事物,我們的 Flink 應用程式必須顯式實作應用程式邏輯來正确處理。

如果您發現自己處于類似情況,請不要擔心有更好的方法!您可以使用它HybridSource 來使存檔和實時資料看起來像一個邏輯源。使用HybridSource,您可以為您的使用者提供一個單一來源,該來源首先從雲存儲存檔中讀取一個主題,然後當存檔耗盡時,自動切換到實時 Kafka 主題。應用程式開發人員隻看到一個邏輯DataStream,他們不必考慮任何底層機制。他們隻需閱讀整個資料曆史即可。

使用HybridSource讀取雲對象存儲資料還意味着您可以利用更多的輸入分區來提高讀取吞吐量。雖然我們的 Kafka 主題之一可能會跨數十個或數百個分區進行分區以支援實時資料的足夠吞吐量,但我們的對象存儲資料集通常每次拆分(例如一天)跨數千個分區進行分區以容納大量曆史資料。出色的對象存儲分區與足夠多的任務管理器相結合,将使 Flink 能夠快速浏覽曆史資料,與直接從分區較差的 Kafka 主題中讀取相同數量的資料相比,可以顯着減少回填時間。

DataStream下面是在 Scala 中使用我們的HybridSourcepowered建立一個的樣子KafkaBackfillSource:

val stream: DataStream[KafkaEvent] =
  KafkaBackfillSource.asDataStream[KafkaEvent](
    "KafkaEventSource",
    "topic-name",
    ...
)           

在代碼片段中,KafkaBackfillSource抽象了存檔的存在(從 Kafka 主題和叢集推斷),以便開發人員可以将所有内容視為單個DataStream.

HybridSource是一個非常強大的構造,如果您需要 Flink 應用程式以有序格式讀取多個異構資料源,則絕對應該考慮。

你去吧!優化大型有狀态 Flink 應用程式的另外 3 個技巧。我們希望您喜歡我們的主要學習内容,并希望它們在您實作自己的 Flink 應用程式時有所幫助。如果您正在尋找更多提示并且還沒有閱讀我們的第一篇部落格,請務必在此處檢視它們。

作者:

Kevin Lam在生産工程部的 Streaming Capabilities 團隊工作。他專注于在 Shopify 使有狀态流處理變得強大和簡單。在業餘時間,他喜歡演奏樂器,并在廚房裡嘗試新食譜。

Rafael Aguiar是 Streaming Capabilities 團隊的進階資料工程師。他對分布式系統和所有事物的大規模分析很感興趣。當他不烤一些自制的比薩餅時,他可能會在戶外迷路。在Linkedin上關注他。

來源:https://shopify.engineering/optimizing-apache-flink-tips-part-two