Redpanda:用C++重寫的Kafka

banq發表於2022-06-07

Redpanda 是對 Kafka 的 C++ 重寫,提供與 Kafka API 的 100% 相容性。Redpanda 不需要 Zookeeper 或 JVM,因此在生產中操作起來不太複雜。因此,更廣泛的開發人員受眾的可訪問性。

您可以在此處找到有關其他安裝選項的更多資訊。

Redpanda是一個基於附加日誌的相容Kafka的分散式流系統。與 Kafka 相比,Redpanda 旨在為使用者提供更低的延遲和更低的操作複雜性。它在內部使用Raft 共識演算法,而不是依賴於Zookeeper的單獨安裝。Redpanda 使用 Kafka 的有線協議:Redpanda 使用常規的 Kafka 客戶端,而不是釋出自己的客戶端庫。

像 Kafka 一樣,Redpanda 提供了一組命名的、部分有序的日誌,稱為主題。每個主題被分片成一個或多個分割槽,每個分割槽是一個完全有序的訊息日誌訊息在分割槽中的位置由唯一的單調遞增整數offset標識,它提供了總順序。偏移量可能是稀疏的:日誌中的一些偏移量用於內部訊息,例如事務後設資料,這些對客戶端是不可見的。

Kafka 客戶端分為幾個部分。使用者使用生產者客戶端向分割槽寫入(產生)訊息,並使用消費者客戶端從分割槽中讀取(輪詢)訊息。使用者可以在手動指定的分割槽中產生和消費,或者允許系統自動選擇訊息寫入哪個分割槽,以及消費者從哪個分割槽讀取。

輪詢訊息不會刪除它們。相反,消費者透過從給定分割槽讀取連續偏移量來模擬“從佇列中消費”。消費者既可以為自己分配特定的分割槽並自己管理偏移量,也可以訂閱一個主題並允許 Redpanda 自動管理分割槽和偏移量。可以將偏移量提交給 Redpanda,它會持久地儲存它們,以便崩潰的消費者可以從他們(或他們的前輩)停止的地方繼續。

Redpanda 實現了 Kafka 的事務協議,但是這種支援在 22.1.1 版本中仍然存在一個功能標誌。Redpanda 沒有為使用者提供有關如何使用事務的具體文件,而是依賴 Kafka 的文件。Kafka 的官方文件規定消費者可以在兩個隔離級別之間進行選擇:read_uncomitted和read_committed. read_uncommitted允許消費者檢視“所有訊息,甚至是已中止的事務訊息”。該read_committed設定“將僅返回已提交的事務性訊息”。

Confluence 的Transactional Messaging wiki 頁面提供了 Kafka 中的事務應用程式所期望的五個簡單要求。起初,對於試圖理解事務語義的使用者來說,這看起來像是一個很有希望的總結:

  1. 原子性:消費者的應用程式不應暴露於來自未提交事務的訊息。
  2. 永續性:經紀人不能丟失任何已提交的交易。
  3. 排序:事務感知消費者應該在每個分割槽內看到原始事務順序中的事務。
  4. 交錯:每個分割槽都應該能夠接受來自事務和非事務生產者的訊息
  5. 事務中不應有重複的訊息。


Redpanda 認為這個 wiki 頁面在這兩點上都是錯誤的。他們指出了一個有點難找的谷歌文件,它作為 Kafka 事務協議的設計文件,其中指出寫入原子性是指作為一個單元的寫入成功或失敗,然後連結到上述wiki 頁面,上面說原子性意味著預防中止讀取。設計文件還提供了原子性無法保持的幾種情況:
  1. 對於壓縮主題,事務的某些訊息可能會被較新版本覆蓋。
  2. 事務可能跨越日誌段。因此,當舊段被刪除時,我們可能會在事務的第一部分丟失一些訊息。
  3. 消費者可能會在交易中尋找任意點,因此會丟失一些初始訊息。
  4. 消費者可能不會從參與事務的所有分割槽中消費。因此,他們將永遠無法讀取構成交易的所有訊息。


考慮到 Kafka 的資料模型,這些都是合理的約束。但是,如果我們不使用壓縮,不尋求任意偏移量,並且從參與事務的所有分割槽中消費,會發生什麼?寫入是否相互隔離?

具體來說:如果事務T 1在T 2之前提交,T 1寫入的所有偏移量是否都在T 2寫入的偏移量之前?許多事務系統緩衝它們的寫入並在提交時或多或少地以原子方式應用它們,但仔細閱讀此設計文件會發現 Kafka 並沒有這樣做。相反,Kafka 選擇在事務中的每個請求發生時立即將寫入新增到日誌中——並且為了保持效能,在事務寫入期間不鎖定分割槽。這意味著來自兩個不同事務的寫入可能會在偏移量中交錯。

那麼消費者處理這些寫入的順序呢?設計文件的消費者部分解釋了訊息總是以偏移順序傳遞。這表明 wiki 是不正確的,事務性寫入應該明顯交錯。

熟悉其他資料庫的讀者可能知道,這些術語有現成的含義:至少從20世紀90年代中期開始,它們就已經被研究和形式化了。在Adya, Liskov, & O'Neil的形式主義中,讀未提交防止了G0現象(寫迴圈)。當兩個(或多個)事務對一個或多個物件的寫入交錯進行時,就會發生寫入迴圈。例如,事務T1在T2寫X之前寫了一些物件,而T2在T1寫Y之前寫了一些Y,給定了寫X和Y的總順序。G1a(中止讀取),G1b(中間讀取),和G1c(迴圈資訊流)。終止讀意味著一個事務觀察到一個沒有提交的事務所寫的值。中間讀涉及從一個事務的中間讀取一個狀態。迴圈資訊流包括事務之間的依賴性迴圈,其中T1在T2寫X之前寫了一些X,或者T2讀取了T1寫的東西。當然,這些反常現象與暫存器上的讀寫歷史有關,而不是日誌,但我們可以想象Kafka的資料模型中的類似現象。

從所有這些來源來看,一個足夠勤奮的讀者可以得出結論,Kafka(因此Redpanda)事務允許G0,在read_committed時禁止G1a,並允許G1b。G1c(涉及寫-寫和寫-讀依賴關係的迴圈)是否會發生仍不清楚。


事務 ID
在 Kafka/Redpanda 中執行事務的每個生產者都必須選擇一個事務 ID:一個含義不明確的字串,但如果選擇不正確,可能會導致事務系統表現出未定義的行為。

Kafka 事務設計文件有一個標題為“事務保證”的部分,詳細說明了事務 ID 如何確保跨會話的冪等性和事務恢復:
當提供這樣的 TransactionalId 時,Kafka 將保證:

  1. 跨應用程式會話的冪等生產。這是透過在具有相同 TransactionalId 的新例項上線時遮蔽舊代來實現的。
  2. 跨應用程式會話的事務恢復。如果應用程式例項死亡,則可以保證下一個例項已完成任何未完成的事務(無論是中止還是提交),從而在恢復工作之前使新例項處於乾淨狀態。


詳細點選標題


 

相關文章