分散式事務(4)---最終一致性方案之TCC

白露非霜發表於2021-12-07

分散式事務(1)-理論基礎

分散式事務(2)---強一致性分散式事務解決方案

分散式事務(3)---強一致性分散式事務Atomikos實戰

強一致性分散式事務解決方案要求參與事務的各個節點的資料時刻保持一致,在高併發場景下,系統的效能可能收到影響。而最終一致性方案並不要求資料時刻一致,允許其存在中間狀態,只要一段時間後資料能夠最終一致即可。

所以基於BASE理論,提出了最終一致性解決方案,典型的有:TCC解決方案,可靠訊息最終一致性方案,最大努力通知型解決方案。

其優點是:

1.效能比較高,不會因為長時間持有事務佔用資源。

2.具備可用性。

3.適合高併發場景。

缺點是,因為資料的短暫不一致,所以會出現某一時刻資料的不一致。

對於一致性特別高的場景不適用。

我們實現最終一致性方案時,需要注意幾個操作:

可查詢操作:業務方需要提供可查詢介面,來查詢資料資訊和狀態,供其他服務知道資料狀態。

冪等操作:同樣的引數執行同一個方法,返回的結果都一樣。在分散式環境,難免會出現資料的不一致,很多時候為了保證資料的一致,我們都會進行重試。如果不保證冪等,即使重試成功了,也無法保證資料的一致性。我們可以通過業務本身實現實現冪等,比如資料庫的唯一索引來約束;也可以快取(記錄)請求和操作結果,當檢測到一樣的請求時,返回之前的結果。

補償操作:某些資料存在不正常的狀態,需要通過額外的方式使資料達到最終一致性的操作。

 

TCC

TCC解決方案主要包括三個階段:try---嘗試業務執行,confirm---確定業務執行,cancel---取消業務執行

try階段完成所有業務的一致性檢查,預留必要的業務資源。

confirm階段,真正執行業務,因為try已經執行了資源預留,所以此階段不會再檢查資料,此階段的操作需要滿足冪等。

cancel階段,釋放try預留的業務資源,此階段也需要滿足冪等。

TCC主要用於跨服務呼叫下分散式事務問題,適用於具有強隔離性,又嚴格要求一致性的業務場景。

舉個例子

還是用下單扣庫存為例

try階段:建立訂單,並將訂單狀態設定為待提交,呼叫庫存服務預扣減庫存。庫存表中庫存欄位減去訂單中的數量,同事在預扣減欄位中增加訂單中庫存數量。以此來預留資源

confirm階段:如果try全部成功,則進入confirm階段。此階段將訂單狀態修改為已提交,庫存服務則將預扣減庫存欄位的數量減去訂單中的數量,實現真正的減庫存。

通常TCC方案我們都認為confirm階段是不會出錯的。就是說只要try成功了,那麼confirm就一定會成功。如果confirm出錯了,那麼就需要引入補償機制或者人工處理。

cancel階段:try階段失敗或者出現異常,至此那個cancel,訂單狀態修改為已取消,庫存服務將表中庫存欄位增加訂單中的數量,預扣減欄位減去訂單中的數量,以此實現事務回滾。同樣TCC中我們認為cancel階段一定會執行成功,如果失敗也需要引入重試或者人工處理。

TCC方案中鎖定資源的粒度小,有利於提高系統效能;confirm和cancel階段的冪等保證分散式事務執行完成後資料的一致性。由主業務放發起事務,無論是主業務還是分支業務都能叢集部署,解決了XA規範的單點故障問題。但是它的程式碼需要耦合到業務中,參與分散式事務的每個業務方法都需要try,confirm,cancel階段,增加開發成本。

 

TCC中需要注意的問題

1.空回滾

當一個分支事務所在的服務發生當機或者網路異常導致呼叫失敗,並未執行try方法,當恢復後事務執行回滾操作就會呼叫此分支事務的cancel方法,如果cancel方法不能處理此種情況就會出現空回滾。

是否出現空回滾,我們需要需要判斷是否執行了try方法,如果執行了就沒有空回滾。解決方法就是當主業務發起事務時,生成一個全域性事務記錄,並生成一個全域性唯一ID,貫穿整個事務,再建立一張分支事務記錄表,用於記錄分支事務,try執行時將全域性事務ID和分支事務ID存入分支事務表中,表示執行了try階段,當cancel執行時,先判斷表中是否有該全域性事務ID的資料,如果有則回滾,否則不做任何操作。比如seata的AT模式中就有分支事務表。

2.冪等問題

由於服務當機或者網路問題,方法的呼叫可能出現超時,為了保證事務正常執行我們往往會加入重試的機制,因此就需要保證confirm和cancel階段操作的冪等性。

我們可以在分支事務記錄表中增加事務執行狀態,每次執行confirm和cancel方法時都查詢該事務的執行狀態,以此判斷事務的冪等性。

3.懸掛問題

TCC中,在呼叫try之前會先註冊分支事務,註冊分支事務之後,呼叫出現超時,此時try請求還未到達對應的服務,因為呼叫超時了,所以會執行cancel呼叫,此時cancel已經執行完了,然而這個時候try請求到達了,這個時候執行了try之後就沒有後續的操作了,就會導致資源掛起,無法釋放。

執行try方法時我們可以判斷confirm或者cancel方法是否執行,如果執行了那麼就不執行try階段。同樣藉助分支事務表中事務的執行狀態。如果已經執行了confirm或者cancel那麼try就執行。

 

相關文章