【乾貨】Kafka 事務特性分析

特性背景面試

消息事務是指一系列的生產、消費操做能夠要麼都完成,要麼都失敗,相似數據庫的事務。這個特性在0.10.2的版本是不支持的,從0.11版本開始才支持。華爲雲DMS率先提供Kafka 1.1.0的專享版服務,支持消息事務特性。算法

支持事務消息有什麼做用?消息事務是實現分佈式事務的一種方案,能夠確保分佈式場景下的數據最終一致性。例如最經常使用的轉帳場景,小王 轉帳到小明,實際操做是小王帳戶減去相應金額,小明的帳戶增長相應金額,在分庫分表的前提下,2個帳戶存儲在不一樣的數據庫中,這時須要分佈式事務才能保證數據庫一致性,單個數據庫的事務沒法保證跨庫之間的原子性。若是小王帳戶先扣錢,再去發送消息到小明所在的數據庫去通知增長錢,在沒有事務消息的狀況下,不管是先扣錢或者先發送通知增長錢,都會有數據不一致的問題,由於沒法保證二者的原子性。而有了事務消息,能夠保證發送通知與本地事務(扣錢)是一個原子操做,本地事務與發送通知能夠同時成功或者同時失敗,確保數據一致。數據庫

除了數據最終一致性外,還實現了消息Exactly once語義。所謂Exactly once語義是消息傳遞語義中最難實現的一種,包括At most once:最多一次(不會重複,可是可能丟失數據); At least once:至少投遞一次(不會丟失,可是會致使重複)和Exactly once: 恰好一次(不丟不重),也即冪等性。Kafka的冪等性能夠保證生產只對一個分區實現Exactl once語義,須要多個分區也實現這個語義,還須要引入消息事務確保原子性。緩存

分佈式事務介紹架構

當前系統架構主流是分佈式架構與微服務架構,在這種架構下數據源不是單一的數據庫,業務邏輯每每須要在多個數據庫中實現原子操做,單個數據庫中的強大的本地事務沒法保證多節點原子操做。 此時須要分佈式事務來確保數據的一致性。目前使用較多的分佈式事務解決方案有幾種:負載均衡

一、XA事務:兩階段/三階段提交分佈式

XA是由X/Open組織提出的分佈式事務的規範。XA規範主要定義了(全局)事務管理器(Transaction Manager)和(局部)資源管理器(Resource Manager)之間的接口。XA接口是雙向的系統接口,在事務管理器(Transaction Manager)以及一個或多個資源管理器(Resource Manager)之間造成通訊橋樑。實現XA事務的關鍵是兩階段和三階段提交協議。ide

兩階段提交協議(Two-phase Commit,2PC)常常被用來實現分佈式事務。通常分爲協調器C和若干事務參與者Si兩種角色,這裏的事務參與者就是具體的數據庫,協調器能夠和事務參與者在一臺機器上,以下圖:微服務

二階段提交協議主要包括由2個階段:第一個階段爲準備階段(prepare),第二階段爲提交階段。準備階段由事務協調者向事務參與者發送prepare消息,各個參與者處理本地事務但不提交,而後向事務協調者返回事務狀態。 提交階段根據準備階段各參與者的執行請求,協調者肯定事務是提交或者回滾,向各個參與者發送命令。性能

二階段提交協議主要的問題是在提交執行過程當中,全部的參與者都須要遵從協調者的統一調度,期間處於阻塞狀態而不能從事其餘操做,這樣效率及其低下。特別是當協調者發出提交通知到部分參與者後宕機,其餘參與者就會阻塞。

針對二階段提交存在的問題,三階段提交協議在prepare與commit階段之間增長一個pre-commit階段。Prepare階段只詢問參與者而不作事務,而在pre-commit階段各個參與者纔會執行本地事務但不提交。Commit階段就是直接提交。這樣作能夠避免二階段當協調者遲遲沒有發出commit或者rollback通知,參與者在超時後能夠自行提交或者回滾,避免阻塞事務(這是由於通過了prepare階段已經確認了各個參與者是能夠執行的,最後第三階段直接執行便可)。 三階段提交也存在不少問題,也不能徹底保證數據一致,徹底一致須要用到Paxos算法。

二、TCC補償性事務解決方案

TCC分別對應Try、Confirm和Cancel三種操做,含義以下:

Try:預留業務資源

Confirm:確認執行業務操做,執行事務

Cancel:取消執行業務操做

TCC解決了跨應用業務操做的原子性問題,在諸如組合支付、帳務拆分場景很是實用。TCC實際上把數據庫層的二階段提交上提到了應用層來實現,對於數據庫來講是一階段提交,規避了數據庫層的2PC性能低下問題。TCC須要業務提供使用,開發複雜和成本高。

三、事務消息

基於消息中間件的事務消息來完成分佈式事務。事務消息能夠確保本地執行事務與消息發送是原子的:先發送一條消息到消息中間件,而後執行本地事務,當本地事務成功後再發送提交確認到消息中間件,而後這條消息才能被其餘業務消費者所能感知,從而確保原子性。

Kafka消息事務

1、基本概念

爲了支持事務,Kafka 0.11.0版本引入如下概念:

1.事務協調者:相似於消費組負載均衡的協調者,每個實現事務的生產端都被分配到一個事務協調者(Transaction Coordinator)。

2.引入一個內部Kafka Topic做爲事務Log:相似於消費管理Offset的Topic,事務Topic自己也是持久化的,日誌信息記錄事務狀態信息,由事務協調者寫入。

3.引入控制消息(Control Messages):這些消息是客戶端產生的並寫入到主題的特殊消息,但對於使用者來講不可見。它們是用來讓broker告知消費者以前拉取的消息是否被原子性提交。

4.引入TransactionId:不一樣生產實例使用同一個TransactionId表示是同一個事務,能夠跨Session的數據冪等發送。當具備相同Transaction ID的新的Producer實例被建立且工做時,舊的且擁有相同Transaction ID的Producer將再也不工做,避免事務僵死。

5.Producer ID:每一個新的Producer在初始化的時候會被分配一個惟一的PID,這個PID對用戶是不可見的。主要是爲提供冪等性時引入的。

6.Sequence Numbler。(對於每一個PID,該Producer發送數據的每一個都對應一個從0開始單調遞增的Sequence Number。

7.每一個生產者增長一個epoch:用於標識同一個事務Id在一次事務中的epoch,每次初始化事務時會遞增,從而讓服務端能夠知道生產者請求是否舊的請求。

8.冪等性:保證發送單個分區的消息只會發送一次,不會出現重複消息。增長一個冪等性的開關enable.idempotence,能夠獨立與事務使用,便可以只開啓冪等但不開啓事務。

2、事務流程

以下圖所示:

一、查找事務協調者

生產者會首先發起一個查找事務協調者的請求(FindCoordinatorRequest)。協調者會負責分配一個PID給生產者。相似於消費組的協調者。

二、獲取produce ID

在知道事務協調者後,生產者須要往協調者發送初始化pid請求(initPidRequest)。這個請求分兩種狀況:

●不帶transactionID

這種狀況下直接生成一個新的produce ID便可,返回給客戶端

●帶transactionID

這種狀況下,kafka根據transactionalId獲取對應的PID,這個對應關係是保存在事務日誌中(上圖2a)。這樣能夠確保相同的TransactionId返回相同的PID,用於恢復或者終止以前未完成的事務。

三、啓動事務

生產者經過調用beginTransaction接口啓動事務,此時只是內部的狀態記錄爲事務開始,可是事務協調者認爲事務開始只有當生產者開始發送第一條消息纔開始。

四、消費和生產配合過程

這一步是消費和生成互相配合完成事務的過程,其中涉及多個請求:

●增長分區到事務請求

當生產者有新分區要寫入數據,則會發送AddPartitionToTxnRequest到事務協調者。協調者會處理請求,主要作的事情是更新事務元數據信息,並把信息寫入到事務日誌中(事務Topic)。

●生產請求

生產者經過調用send接口發送數據到分區,這些請求新增pid,epoch和sequence number字段。

●增長消費offset到事務

生產者經過新增的snedOffsets ToTransaction接口,會發送某個分區的Offset信息到事務協調者。協調者會把分區信息增長到事務中。

●事務提交offset請求

當生產者調用事務提交offset接口後,會發送一個TxnOffsetCommitRequest請求到消費組協調者,消費組協調者會把offset存儲在__consumer-offsets Topic中。協調者會根據請求的PID和epoch驗證生產者是否容許發起這個請求。 消費offset只有當事務提交後纔對外可見。

五、提交或回滾事務

用戶經過調用commitTransaction或abortTranssaction方法提交或回滾事務。

●EndTxnRequest

當生產者完成事務後,客戶端須要顯式調用結束事務或者回滾事務。前者會使得消息對消費者可見,後者會對生產數據標記爲Abort狀態,使得消息對消費者不可見。不管是提交或者回滾,都是發送一個EndTnxRequest請求到事務協調者,寫入PREPARE_COMMIT或者PREPARE_ABORT信息到事務記錄日誌中(5.1a)。

●WriteTxnMarkerRequest

這個請求是事務協調者向事務中每一個TopicPartition的Leader發送的。每一個Broker收到請求後會寫入COMMIT(PID)或者ABORT(PID)控制信息到數據日誌中(5.2a)。

這個信息用於告知消費者當前消息是哪一個事務,消息是否應該接受或者丟棄。而對於未提交消息,消費者會緩存該事務的消息直到提交或者回滾。

這裏要注意,若是事務也涉及到__consumer_offsets,即該事務中有消費數據的操做且將該消費的Offset存於__consumer_offsets中,Transaction Coordinator也須要向該內部Topic的各Partition的Leader發送WriteTxnMarkerRequest從而寫入COMMIT(PID)或COMMIT(PID)控制信息(5.2a 左邊)。

●寫入最終提交或回滾信息

當提交和回滾信息寫入數據日子後,事務協調者會往事務日誌中寫入最終的提交或者終止信息以表示事務已經完成(圖5.3),此時大部分於事務有關係的消息均可以被刪除(經過標記後面在日誌壓縮時會被移除),咱們只須要保留事務ID以及其時間戳便可。

接口

示例

歡迎工做一到五年的Java工程師朋友們加入Java架構開發:744677563

本羣提供免費的學習指導 架構資料 以及免費的解答

不懂得問題均可以在本羣提出來 以後還會有職業生涯規劃以及面試指導

相關文章
相關標籤/搜索