今天,我們來聊聊分佈式事務

前言

我們都知道數據庫的事務滿足"ACID"特性,A是指事務的原子性,C是指事務的一致性,I指事務的隔離性,D指持久性。

最開始我們的數據量都很小,所有的數據都落在一個數據庫中。MySQL數據庫單表的最大數據量在百萬條左右,隨着系統變大,數據越來越多,這個時候我們不得不將數據分佈在不同的數據庫中存放,也就是常說的數據分片(sharding)。我們可以通過一定的分庫策略將同一個交易鏈路上的數據放到一個數據庫中,例如我們可以將一個訂單所有產生的數據放到一個數據庫中,按照訂單號來分庫,這樣我們在生成訂單相關數據的時候可以在單個數據庫上開啓事務來完成。

這樣看似完美的解決方案其實並不完美。例如我們想記錄用戶維度的訂單數據時,這個方案就無能爲力了。於是分佈式事務應運而生。

分佈式系統CAP BASE理論

說到分佈式系統,不得不說CAP和BASE理論,它是指導我們分佈式系統的理論基礎。

CAP理論

加州大學伯克利分校Eric Brewer教授支持一個分佈式系統無法滿足這樣三條特性:

Consistency,一致性:多個操作同時生效,不會出現部分生效的情況

Availability,可用性:客戶端的每個請求在服務端能夠正確被響應

Partition tolerance,分區容錯性:分區中部分節點掛了不會影響整體服務可用性,這也是分佈式系統最基本的要求

分區容錯性是一個分佈式系統最基本的要求,因此一般分佈式系統都會滿足分區容錯性,否則就失去了分佈式系統的意義。分佈式系統一般會在一致性和可用性上做出取捨。例如犧牲一致性換區可用性,這裏說的犧牲一致性是指犧牲掉系統的"強一致性",最終我們的系統還是一致的,即所謂的"弱一致性"或者"最終一致性"。當我們的系統需要保證強一致性時我們不得不犧牲掉可用性:當系統部分節點延遲或者down機整個系統的服務將變得不可用,也因此係統數據是強一致性的。

BASE理論

BASE理論是對CAP理論的進一步擴充:

Basically Available(基本可用)

Soft state(軟狀態)

Eventually consistent(最終一致性)

基本可用是指我們的系統無法做到百分百可用,但是可以保證例如"3個9"(99.9%)可用。軟狀態是指允許我們的系統存在中間狀態,在最終我們的系統可以達到最終一致性。BASE理論強調的是系統的最終一致性。

分佈式事務

目前分佈式事務的解決方案主要有:二階段提交(2PC)、柔性補償事務(TCC)、本地消息表(異步確保)、MQ事務消息等。

二階段提交

二階段事務分爲兩個階段:階段一事務協調者通知各個節點執行事務預提交;階段二協調者根據各個節點的響應來通知各個節點提交或者回滾事務操作。

兩階段提交這種解決方案屬於犧牲了一部分可用性來換取的一致性。

優點: 儘量保證了數據的強一致,適合對數據強一致要求很高的關鍵領域。(其實也不能100%保證強一致)

缺點: 實現複雜,犧牲了可用性,同步阻塞對性能影響較大,不適合高併發高性能場景。

補償事務(TCC)

TCC 其實就是採用的補償機制,其核心思想是:針對每個操作,都要註冊一個與其對應的確認和補償(撤銷)操作。它分爲三個階段:

Try 階段主要是對業務系統做檢測及資源預留

Confirm 階段主要是對業務系統做確認提交,Try階段執行成功並開始執行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。

Cancel 階段主要是在業務執行錯誤,需要回滾的狀態下執行的業務取消,預留資源釋放。

舉個例子,假入 Bob 要向 Smith 轉賬,思路大概是:

我們有一個本地方法,裏面依次調用

首先在 Try 階段,要先調用遠程接口把 Smith 和 Bob 的錢給凍結起來。

在 Confirm 階段,執行遠程調用的轉賬的操作,轉賬成功進行解凍。

如果第2步執行成功,那麼轉賬成功,如果第二步執行失敗,則調用遠程凍結接口對應的解凍方法 (Cancel)。

優點: 跟2PC比起來,實現以及流程相對簡單了一些,但數據的一致性比2PC也要差一些

缺點: 缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬於應用層的一種補償方式,所以需要程序員在實現的時候多寫很多補償的代碼,在一些場景中,一些業務流程可能用TCC不太好定義及處理。

本地消息表(異步確保)

本地消息表這種實現方式應該是業界使用最多的,其核心思想是將分佈式事務拆分成本地事務進行處理,這種思路是來源於ebay。我們可以從下面的流程圖中看出其中的一些細節:

基本思路就是:

消息生產方,需要額外建一個消息表,並記錄消息發送狀態。消息表和業務數據要在一個事務裏提交,也就是說他們要在一個數據庫裏面。然後消息會經過MQ發送到消息的消費方。如果消息發送失敗,會進行重試發送。

消息消費方,需要處理這個消息,並完成自己的業務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那麼就會重試執行。如果是業務上面的失敗,可以給生產方發送一個業務補償消息,通知生產方進行回滾等操作。

生產方和消費方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發送一遍。如果有靠譜的自動對賬補賬邏輯,這種方案還是非常實用的。

這種方案遵循BASE理論,採用的是最終一致性,筆者認爲是這幾種方案裏面比較適合實際業務場景的,即不會出現像2PC那樣複雜的實現(當調用鏈很長的時候,2PC的可用性是非常低的),也不會像TCC那樣可能出現確認或者回滾不了的情況。

優點: 一種非常經典的實現,避免了分佈式事務,實現了最終一致性。

缺點: 消息表會耦合到業務系統中,如果沒有封裝好的解決方案,會有很多雜活需要處理。

MQ事務消息

有一些第三方的MQ是支持事務消息的,比如RocketMQ,他們支持事務消息的方式也是類似於採用的二階段提交,但是市面上一些主流的MQ都是不支持事務消息的,比如 RabbitMQ 和 Kafka 都不支持。

以阿里的 RocketMQ 中間件爲例,其思路大致爲:

第一階段Prepared消息,會拿到消息的地址。

第二階段執行本地事務,第三階段通過第一階段拿到的地址去訪問消息,並修改狀態。

也就是說在業務方法內要想消息隊列提交兩次請求,一次發送消息和一次確認消息。如果確認消息發送失敗了RocketMQ會定期掃描消息集羣中的事務消息,這時候發現了Prepared消息,它會向消息發送者確認,所以生產方需要實現一個check接口,RocketMQ會根據發送端設置的策略來決定是回滾還是繼續發送確認消息。這樣就保證了消息發送與本地事務同時成功或同時失敗。

優點: 實現了最終一致性,不需要依賴本地數據庫事務。

缺點: 實現難度大,主流MQ不支持。

MQ事務消息

總結

目前分佈式事務主要有:二階段提交、TCC柔性事務、本地消息表(異步確保)和MQ事務消息等解決方案。二階段提交可以確保數據的強一致性,其他的解決方案屬於最終一致性解決方案。

分佈式事務雖然能保證數據的強一致性,但是性能低。使用前需要確定是否真的需要分佈式事務。

現在加羣即可獲取Java工程化、高性能及分佈式、高性能、高架構、zookeeper、性能調優、Spring、MyBatis、Netty源碼分析和大數據等多個知識點高級進階乾貨的直播免費學習權限及相關視頻資料,羣號:923116658

點擊鏈接加入羣聊【Java架構解析】:https://jq.qq.com/?_wv=1027&k=5e1QsXb

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