怎麼用消息隊列實現分佈式事務?

當消息隊列和事務聯繫在一起時,它指的是消息生產者和消息消費者之間如何保持數據一致性。

什麼是分佈式事務?

事務是指當我們進行若干項數據更新操作時,爲了保證數據的完整性和一致性,我們希望這些更新操作要麼都成功,要麼都失敗。而更新的數據,並不侷限於數據庫中的數據,它可以是磁盤上的文件,也可以是一個遠程服務,或者以其他形式存儲的數據。

事務會有四個特性,俗稱ACID:

  • 原子性(A),一個事務操作不可分割,要麼成功,要麼失敗。
  • 一致性(C),指數據在事務執行完成的時間之前,讀到的一定是更新前的數據,之後讀到的一定是更新後的數據,不存在一個時刻,讓用戶讀到更新過程中的數據。
  • 隔離性(I),一個事務的執行不應該被其他事務干擾。
  • 持久性(D),一個事務一旦完成提交,後續的其他操作和故障都不會對事務的結果產生影響。

對於分佈式系統來說,嚴格實現ACID幾乎是不可能的,或者說代價太過,因此,我們在保證可用性和不嚴重犧牲性能的前提下,實現數據一致性已經很困難,因此,出現了很多變種,例如順序一致性、最終一致性等。

目前大家所說的分佈式事務,更多情況下,是在分佈式系統中事務的不完整實現,在不同的應用場景中,有不同的實現,目的都是通過一些妥協來解決實際問題。

在實際應用中,比較常見的分佈式事務有2PC(Two-phase Commit,二階段提交)、TCC(Try-Confirm-Cancel)和事務消息。

事務消息使用的場景主要是那些需要異步更新數據,並且對數據實時性要求不太高的場景。

消息隊列如何實現分佈式事務?

事務消息需要消息隊列提供相應的功能才能實現,Kafka和RocketMQ都提供了事務相關功能。

下面以下訂單爲例,說明一下如何使用消息隊列實現分佈式事務。

首先,訂單系統在消息隊列上開啓一個事務,然後訂單系統給消息服務器發送一個“半消息”,這個半消息不是說消息內容不完整,它包含完整的消息內容。半消息和普通消息的唯一區別,是在事務提交之前,對於消費者來說,這個消息時不可見的。

半消息發送成功後,訂單系統就可以執行本地事務了,在訂單中創建一條訂單記錄,並提交訂單庫的數據庫事務。然後根據本地事務的執行結果決定提交或者回滾事務消息。如果訂單創建成功,那麼就提交事務消息,購物車系統就可以消費到這條消息繼續後續的流程。如果訂單創建失敗,就回滾消息,購物車系統就不會受到這條消息。這樣基本實現了“要麼都成功,要麼都失敗”的一致性要求。

那麼當提交事務消息時失敗了,應該怎麼處理呢?對於這種情況,Kakfa的解決方案比較簡單粗暴,直接拋出異常,讓用戶自行處理。RocketMQ會使用事務反查的機制來解決事務消息提交失敗的問題。訂單系統作爲Producer,在提交或者回滾事務消息時發生網絡異常,RocketMQ的Broker沒有收到提交或者回滾的請求,Broker會定期去Producer上反查這個事務對應的本地事務的狀態,然後根據反查結果決定提交或者回滾這個事務。

我們的業務代碼中需要實現一個反查本地事務狀態的街口,告知RocketMQ本地事務是成功還是失敗。

下面是用RocketMQ進行事務處理的流程圖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章