如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

banq發表於2021-10-14

通過了解 Apache Kafka 如何對資料進行排序,您可以確保您的資料或應用程式保持良好的工作狀態。

儘管Apache Kafka已經贏得了作為功能強大的分散式流媒體平臺的聲譽,但在確保按您希望的順序儲存和檢索資料方面,它還具有一些複雜性。

為了捕獲流資料,Kafka 將記錄釋出到多個 Kafka消費者可以訂閱和檢索資料的主題、類別或提要名稱。Kafka 叢集為每個主題維護一個分割槽日誌,來自同一個生產者的所有訊息傳送到同一個分割槽,並按照它們到達的順序新增。通過這種方式,分割槽是結構化的提交日誌,儲存有序且不可變的記錄序列。新增到分割槽的每條記錄都分配了一個偏移量,一個唯一的順序 ID。

在 Kafka 中按照你喜歡的順序接收資料的挑戰有一個相對簡單的解決方案:分割槽保持嚴格的順序,並且總是按照資料新增到分割槽的順序將資料傳送給消費者。但是,Kafka 不會維護跨多個分割槽的主題的總記錄順序。

讓我們使用一個”鍵key“的例子,它允許你向生產者記錄新增鍵。我們將向一個有兩個分割槽的Kafka主題傳送四個包含鍵key的訊息。

有四個不同的鍵——Costco、Walmart、Target 和 Best Buy,並在叢集中雜湊並分佈到分割槽中,

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

現在我們將再傳送另外四條訊息,Kafka 將向已經使用現有Key的分割槽傳送訊息:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

所有 Costco 或 Walmart 記錄都在分割槽 1 中,所有 Target 或 Best Buy 記錄都在分割槽 2 中。這些記錄按它們傳送到這些分割槽的順序排列。

接下來,讓我們看看如果向叢集新增更多分割槽會發生什麼,我們可能希望這樣做以便以更健康的方式平衡資料。

新增新的一個分割槽後,我們將觸發重新平衡事件:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

資料保持良好的鍵結構,所有 Best Buy 資料都平衡到分割槽 3。如果我們向主題新增另外四條訊息,情況將保持不變:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

資料進入儲存已建立key的分割槽。但是,因為一個分割槽儲存的資料是其他分割槽的兩倍,所以新增第四個分割槽並觸發另一個重新平衡是合乎邏輯的:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

結果是資料集的健康平衡。

確保資料始終按順序傳送

 

  

其他問題

還有許多其他問題可能導致資料在 Kafka 中無序到達,包括代理或客戶端故障以及重新嘗試傳送資料產生的混亂。為了解決這些問題,讓我們首先仔細看看 Kafka 生產者。

以下是顯示 Kafka 生產者如何工作的高階概述:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

至少,ProducerRecord 物件包括要將資料傳送到的主題和一個值。它還可以包括要使用的指定分割槽和Key。在上面的示例中,我建議您始終使用Key。如果不這樣做,資料將迴圈分配到任何沒有組織的分割槽。ProducerRecord 中的資料接下來使用 Serializer 進行編碼,然後 Partitioner 演算法決定資料的去向。

上圖左側概述的重試機制是一個經常發生資料順序問題的區域。例如,假設您嘗試向 Kafka 傳送兩條記錄,但一條由於網路問題而失敗,而另一條通過。當您嘗試重新傳送資料時,存在資料亂序的風險,因為您現在同時向 Kafka 傳送兩個請求。

您可以通過將 max.in.flight.requests.per.connection 設定為 1 來解決此問題。如果設定為多個(並且 retries 引數非零),則代理可能無法成功寫入第一批訊息同時寫第二個,因為它也被允許在飛行中,然後成功重試第一批,將他們的順序交換到你不想要的順序。相比之下,將 max.in.flight.requests.per.connection 設定為 1 可確保這些請求按順序依次發生。

在順序至關重要的場景中,我建議將 in.flight.requests.per.session 設定為 1;這可確保在重試訊息批處理時不會傳送其他訊息。然而,這種策略嚴重限制了生產者的吞吐量,只有在順序必不可少的情況下才應該使用。將允許的重試設定為零似乎是一種可能的選擇,但是,如果對系統可靠性的影響使其成為不可選項。

 

實現“恰好一次的訊息傳遞”

Kafka 包括三種不同的訊息傳遞方法,每種方法都有自己的保證行為:

  • At-Once Message Delivery:此方法將傳遞一次訊息批處理,或從不傳遞。這消除了重新傳送相同訊息的風險,但也允許它們丟失。

  • At-Least-Once Message Delivery:此方法在訊息傳遞之前不會停止。雖然傳遞總是成功並且沒有訊息丟失,但是它們可以被多次傳遞。

  • Exactly-Once Message Delivery:這種方法保證所有訊息的傳遞,並且每個訊息只傳遞一次。雖然會發生失敗和重試,但 Exactly-Once Message Delivery 會採取額外的步驟來確保單次成功傳遞。

顯然,Exactly-Once Message Delivery 是保持資料順序的理想選擇。

將 Exactly-Once Message Delivery 付諸實踐需要利用三個元件:冪等生產者、跨分割槽事務和事務消費者。

  • 1) 冪等生產者

生產者冪等性可以導致訊息在單個程式中持續存在,從而防止重試問題。啟用冪等性會為每個 Kafka 訊息新增一個生產者 ID (PID) 和一個序列 ID。當代理或客戶端發生故障並嘗試重試時,主題僅接受具有從未見過的生產者和序列 ID 的訊息。代理進一步保證了冪等性,它自動對生產者傳送的所有訊息進行重複資料刪除。

  • 2)跨分割槽事務

事務可以確保每條訊息只處理一次。這允許將選定的訊息轉換並原子地寫入多個主題或分割槽,以及偏移量跟蹤消耗的訊息。

原子寫入的狀態由事務協調器和事務日誌(在 Apache Kafka v0.11 中引入)維護。事務協調器類似於消費者組協調器:每個生產者都有一個分配的事務協調器,負責分配PID和管理事務。事務日誌是所有事務的持久記錄,充當事務協調器的狀態儲存。

  • 3) 事務消費者

要強制事務消費者只讀取已提交的資料,請將isolation.level 設定為read_committed(預設情況下,隔離級別為未提交讀。)

 

使用Exactly-Once 訊息傳遞的 Kafka 事務工作流的步驟

下圖捕獲了實現 Exactly-Once Message Delivery 所需的 Kafka 事務工作流步驟。

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

  • 步驟 1 – initTransactions()向事務協調器註冊一個事務transaction ID(一個唯一的永續性生產者 ID)。
  • 第 2 步 - 協調器提高生產者 ID 的紀元(確保只有一個合法的生產者活動例項)。不再接受來自該 PID 先前例項的寫入。
  • 步驟 3 – 在向分割槽傳送資料之前,生產者使用協調器新增一個新分割槽。
  • 步驟 4 – 協調器將每個事務的狀態儲存在記憶體中並將其寫入事務日誌。
  • 步驟 5 – 生產者將訊息傳送到分割槽。
  • 步驟 6 – 生產者開始提交事務,使協調器啟動其兩階段提交協議。
  • 步驟 7 –(提交協議階段 1)協調器通過更新事務日誌來準備提交。
  • 步驟 8 –(提交協議階段 2)協調器將事務提交標記寫入事務中涉及的主題分割槽。
  • 步驟 9 – 協調器將事務標記為已提交。
  • 第 10 步——“Exactly-Once Message Delivery”事務成功。

下面是這個過程的更多技術架構圖:

如何克服 Apache Kafka中的資料順序問題 - DATAVERSITY

通過了解 Apache Kafka 如何對資料進行排序並利用上述技術,您可以確保您的資料或應用程式保持良好的工作狀態。

 

相關文章