java實現分布式事務的三種方案


問題描述:

用戶支付完成會將支付狀態及訂單狀態保存在訂單數據庫中,由訂單服務去維護訂單數據庫。由庫存服務去維護庫存數據庫的信息。下圖是系統結構圖:

 如何實現兩個分布式服務(訂單服務、庫存服務)共同完成一件事即訂單支付成功自動減庫存,這里的關鍵是如何保證兩個分布式服務的事務的一致性。
嘗試解決上邊的需求,在訂單服務中遠程調用減庫存接口,偽代碼如下:

1
2
3
4
5
訂單支付結果通知方法{
 
​ 更新支付表中支付狀態為“成功”。
​ 遠程調用減庫存接口減庫存。

問題如下:
1、如果更新支付表失敗則拋出異常,不再執行遠程調用,此設想沒有問題。
2、如果更新支付表成功,網絡遠程調用超時會拉長本地數據庫事務時間,影響數據庫性能。
3、如果更新支付表成功,遠程調用減庫存成功(減庫存數據庫commit成功),最后更新支付表commit失敗,此時出現操作不一致。(這種情況是訂單服務掛了)

上邊的問題涉及到分布式事務控制。

什么是分布式事務

在分布式系統中一次操作由多個系統協同完成,這種一次事務操作涉及多個系統通過網絡協同完成的過程稱為分布式事務。

理解CAP理論

CAP理論是分布式事務處理的理論基礎:分布式系統在設計時只能在一致性(Consistency)、可用性(Availability)、分區容忍性(PartitionTolerance)中滿足兩種,無法兼顧三種。

一致性(Consistency):服務A、B、C三個結點都存儲了用戶數據, 三個結點的數據需要保持同一時刻數據一致性。
可用性(Availability):服務A、B、C三個結點,其中一個結點宕機不影響整個集群對外提供服務,如果只有服務A結點,當服務A宕機整個系統將無法提供服務,增加服務B、C是為了保證系統的可用性。
分區容忍性(Partition Tolerance):分區容忍性就是允許系統通過網絡協同工作,分區容忍性要解決由於網絡分區導致數據的不完整及無法訪問等問題。

分布式系統不可避免的出現了多個系統通過網絡協同工作的場景,結點之間難免會出現網絡中斷、網延延遲等現象,這種現象一旦出現就導致數據被分散在不同的結點上,這就是網絡分區。

分布式系統如何兼顧CAP:

在保證分區容忍性的前提下一致性和可用性無法兼顧,如果要提高系統的可用性就要增加多個結點,如果要保證數據的一致性就要實現每個結點的數據一致,結點越多可用性越好,但是數據一致性越差。
所以,在進行分布式系統設計時,同時滿足“一致性”、“可用性”和“分區容忍性”三者是幾乎不可能的。
CAP有哪些組合方式?

CA:放棄分區容忍性,加強一致性和可用性,關系數據庫按照CA進行設計。
AP:放棄一致性,加強可用性和分區容忍性,追求最終一致性,很多NoSQL數據庫按照AP進行設計。
說明:這里放棄一致性是指放棄強一致性,強一致性就是寫入成功立刻要查詢出最新數據。追求最終一致性是指允許暫時的數據不一致,只要最終在用戶接受的時間內數據 一致即可。
CP:放棄可用性,加強一致性和分區容忍性,一些強一致性要求的系統按CP進行設計,比如跨行轉賬,一次轉賬請求要等待雙方銀行系統都完成整個事務才算完成。說明:由於網絡問題的存在CP系統可能會出現待等待超時,如果沒有處理超時問題則整個系統會出現阻塞。

總結

​ 在分布式系統設計中AP的應用較多,即保證分區容忍性和可用性,犧牲數據的強一致性(寫操作后立刻讀取到最新數據),保證數據最終一致性。比如:訂單退款,今日退款成功,明日賬戶到賬,只要在預定的用戶可以接受的時間內退款事務走完即可。

分布式事務一致性解決方案

1、兩階段提交協議(2PC)

兩階段提交協議(2 Phase Commitment Protocol),兩階段提交由協調者和參與者組成,共經過兩個階段和三個操作,部分關系數據庫如Oracle、MySQL支持兩階段提交協議

2PC協議流程圖:

1)第一階段:准備階段(prepare)
協調者通知參與者准備提交,各參與者反饋事務執行結果,但參與者先不提交事務。
2)第二階段:提交(commit)/回滾(rollback)階段
協調者通知參與者開始提交,各參與者反饋事務提交結果。只要在這兩個階段中執行結果和提交結果有失敗回復,整個事務回滾。

一個下單減庫存的例子:

1、應用程序連接兩個數據源。
2、應用程序通過事務協調器向兩個庫發起prepare,兩個數據庫收到消息分別執行本地事務(記錄日志),但不提交,如果執行成功則回復yes,否則回復no。
3、事務協調器收到回復,只要有一方回復no則分別向參與者發起回滾事務,參與者開始回滾事務。
4、事務協調器收到回復,全部回復yes,此時向參與者發起提交事務。如果參與者有一方提交事務失敗則由事務協調器發起回滾事務。

2PC的優點:實現強一致性,部分關系數據庫支持(Oracle、MySQL等)。
缺點:整個事務的執行需要由協調者在多個節點之間去協調,增加了事務的執行時間,性能低下。

2、事務補償 TCC

以下單扣庫存為例,Try 階段去占庫存,Confirm 階段則實際扣庫存,如果庫存扣減失敗 Cancel 階段進行回滾,釋放庫存。

TCC事務補償是基於2PC實現的業務層事務控制方案,它是TryConfirmCancel三個單詞的首字母,含義如下:
1、Try 檢查及預留業務資源完成提交事務前的檢查,並預留好資源。
2、Confirm確定執行業務操作對try階段預留的資源正式執行。
3、Cancel取消執行業務操作對try階段預留的資源釋放。

下邊用一個下單減庫存的業務為例來說明:

1、Try
下單業務由訂單服務和庫存服務協同完成,在try階段訂單服務和庫存服務完成檢查和預留資源。
訂單服務檢查當前是否滿足提交訂單的條件(比如:當前存在未完成訂單的不允許提交新訂單)。
庫存服務檢查當前是否有充足的庫存,並鎖定資源。(try階段要檢查訂單服務和庫存服務、預留資源並鎖定資源)
2、Confirm
訂單服務和庫存服務成功完成Try后開始正式執行資源操作。
訂單服務向訂單寫一條訂單信息。
庫存服務減去庫存。
3、Cancel
如果訂單服務和庫存服務有一方出現失敗則全部取消操作。
訂單服務需要刪除新增的訂單信息。
庫存服務將減去的庫存再還原。

優點:最終保證數據的一致性,在業務層實現事務控制,靈活性好。

缺點:開發成本高,每個事務操作每個參與者都需要實現try/confirm/cancel三個接口。

什么是冪等性?

冪等性是指同一個操作無論請求多少次,其結果都相同。
冪等操作實現方式有:
1、操作之前在業務方法進行判斷如果執行過了就不再執行。
2、緩存所有請求和處理的結果,已經處理的請求則直接返回結果。
3、在數據庫表中加一個狀態字段(未處理,已處理),數據操作時判斷未處理時再處理

3、消息隊列實現最終一致性

消息事務其實就是基於消息中間件的兩階段提交,將本地事務和發消息放在同一個事務里,保證本地操作和發送消息同時成功

本方案是將分布式事務拆分成多個本地事務來完成,並且由消息隊列異步協調完成,如下圖

下邊以下單減少庫存為例來說明:

1、訂單服務和庫存服務完成檢查和預留資源。

2、訂單服務在本地事務中完成添加訂單表記錄和添加“減少庫存任務消息”。

3、由定時任務根據消息表的記錄發送給MQ通知庫存服務執行減庫存操作。

4、庫存服務執行減少庫存,並且記錄執行消息狀態(為避免重復執行消息,在執行減庫存之前查詢是否執行過此消息)。

5、庫存服務向MQ發送完成減少庫存的消息。

6、訂單服務接收到完成庫存減少的消息后刪除原來添加的“減少庫存任務消息”。

實現最終事務一致要求:預留資源成功理論上要求正式執行成功,如果執行失敗會進行重試,要求業務執行方法實現冪等。

優點 :由MQ按異步的方式協調完成事務,性能較高。
不用實現try/confirm/cancel接口,開發成本比TCC低。
缺點:此方式基於關系數據庫本地事務來實現,會出現頻繁讀寫數據庫記錄,浪費數據庫資源,另外對於高並發操作不是最佳方案。

4、阿里的分布式框架seata(待了解)


免責聲明!

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



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