分布式事務方案整合


分布式事務方案整合

通過幾天的資料查找,對解決分布式事務的方法有兩階段提交、支付寶分享的TCCtry-confirm-cancel)和基於消息的最終一致解決方案,其中第一條和第二條雖然也能解決問題,但普遍對第三種基於消息隊列的最終一致解決方案推薦多比較高,所以第一條和第二條可以參考使用。

一、設計原則

業務拆分,架構設計時應“盡量避免”分布式事務,如果實在避免不了(這已經是高並發、用戶量比較多的網站了)則使用“最終一致性”處理。

二、設計原理

2.1 CAP原理

  C(一致性)一致性是指數據的原子性,在經典的數據庫中通過事務來保障,事務完成時,無論成功或回滾,數據都會處於一致的狀態,在分布式環境下,一致性是指多個節點數據是否一致;

  A(可用性)服務一直保持可用的狀態,當用戶發出一個請求,服務能在一定的時間內返回結果;

  P(分區容忍性)在分布式應用中,可能因為一些分布式的原因導致系統無法運轉,好的分區容忍性,使應用雖然是一個分布式系統,但是好像一個可以正常運轉的整體

2.2 BASE原理

  1. BA: Basic Availability 基本業務可用性;

  2. S: Soft state 柔性狀態,中間狀態;

  3. E: Eventual consistency 最終一致性;

三、設計方案

3.1 兩階段提交

兩階段提交分為准備階段和提交階段兩個階段,其原理架構圖如下:

3-1

概述:在提交事務的過程中需要在多個節點之間進行協調,而各節點對鎖資源的釋放必須等到事務最終提交時,比較耗時,鎖資源發生沖突的概率增加,當事務的並發量達到一定數量的時候,就會出現大量事務積壓甚至出現死鎖,系統性能就會嚴重下滑。

3.2 TCCTry-Confirm-Cancel

TCC分三部分,如下所述:

1. Try: 嘗試執行業務;

2. Confirm: 確認執行業務;

3. Cancel: 取消執行業務;

此方案開始由支付寶提出來的一種方案,在開源社區github上找到一個TCC型事務java實現(https://github.com/changmingxie/tcc-transaction),部署到本機環境測試了一下demo,可以實現分布式事務補償機制,由於時間倉促,還沒有來得及研究源碼。

優點:現成的框架時間,使用不難,有開發使用文檔,可以為我們以后設計分布式事務時提供一種設計思路和參考。

缺點:不是很成熟,關注度不是很高,可能會存在一些問題。

3.3 基於事務型消息隊列的最終一致性

借助消息隊列,在處理業務邏輯的地方,發送消息,業務邏輯處理成功后,提交消息,確保消息是發送成功的,之后消息隊列投遞來進行處理,如果成功,則結束,如果沒有成功,則重試,直到成功,不過僅僅適用業務邏輯中,第一階段成功,第二階段必須成功的場景。架構圖如下C流程:

 

圖3-3

3.4 基於消息隊列+定時補償機制的最終一致性

前面部分和上面基於事務型消息的隊列,不同的是,第二階段重試的地方,不再是消息中間件自身的重試邏輯了,而是單獨的補償任務機制。其實在大多數的邏輯中,第二階段失敗的概率比較小,所以單獨獨立補償任務表出來,可以更加清晰,能夠比較明確的直到當前多少任務是失敗的。對應上圖的E流程。

至於如何實現冪等性操作,可以通過增加一個message_applied(msg_id)記錄被成功應用的消息,在事務完成之后刪除記錄即可。

 

舉個例子。假設系統中有以下兩個表

  user(id, name, amt_sold, amt_bought)

  transaction(xid, seller_id, buyer_id, amount)

其中user表記錄用戶交易匯總信息,transaction表記錄每個交易的詳細信息。

 

這樣,在進行一筆交易時,若使用事務,就需要對數據庫進行以下操作:

begin;

  INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);

  UPDATE user SET amt_sold = amt_sold + $amount WHERE id = $seller_id;

  UPDATE user SET amt_bought = amt_bought + $amount WHERE id = $buyer_id;

commit;

即在transaction表中記錄交易信息,然后更新賣家和買家的狀態。假設transaction表和user表存儲在不同的節點上,那么上述事務就是一個分布式事務

則使用基於消息隊列的最終一致解決方案的偽代碼如下所示:

第一步:

begin;

    INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);

   put_to_queue "update user("seller", $seller_id, amount);

   put_to_queue "update user("buyer", $buyer_id, amount);

commit;

第二步:

for each message in queue

  begin;

    SELECT count(*) as cnt FROM message_applied WHERE msg_id = message.id;

    if cnt = 0 then

      if message.type = "seller" then

        UPDATE user SET amt_sold = amt_sold + message.amount WHERE id = message.user_id;

      else

        UPDATE user SET amt_bought = amt_bought + message.amount WHERE id = message.user_id;

      end

      INSERT INTO message_applied VALUES(message.id);

    end

  commit;

第三步:

  if 上述事務成功

    dequeue message

    DELETE FROM message_applied WHERE msg_id = message.id;

  end

end

 

四、最佳實踐

根據不同的業務場景對一致性、可用性和分區容忍性的要求不同,在此三者間尋找一個最佳的平衡點來滿足不同的業務場景,可以根據以上方案靈活選擇,不局限於某一種方案。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM