天天看點

分布式事務解決方案之TCC

目錄

一、什麼是TCC

二、TM 事務管理器

三、TCC解決方案

四、TCC需要注意的問題

一、什麼是TCC

TCC是Try、Confirm、Cancel三個詞語的縮寫,TCC要求每個分支事務實作三個操作:預處理Try、确認Confirm、撤銷Cancel。

Try 操作做業務檢查及資源預留 , Confirm做業務确認操作 , Cancel實作一個與Try相反的操作即復原操作 。

TM首先發起所有的分支事務的try操作,任何一個分支事務的try操作執行失敗,TM将會發起所有分支事務的Cancel操作,若try操作全部成功,TM将會發起所有分支事務的Confifirm操作,其中Confirm/Cancel操作若執行失敗,TM會進行重試。

成功情況:

分布式事務解決方案之TCC

失敗情況:

分布式事務解決方案之TCC

TCC分為三個階段

  • Try 階段是做業務檢查(一緻性)及資源預留(隔離) ,此階段僅是一個初步操作,它和後續的Confirm 一起才能真正構成一個完整的業務邏輯。
  • Confirm 階段是做 确認送出 ,Try階段所有分支事務執行成功後開始執行 Confirm。通常情況下,采用TCC則認為 Confirm階段是不會出錯的。即: 隻要Try成功,Confirm一定成功 。若Confirm階段真的出錯了,需引入重試機制或人工處理。
  • Cancel 階段是在業務執行錯誤 需要復原的狀态下執行分支事務的業務取消 ,預留 資源釋放。通常情況下,采用TCC則 認為Cancel階段也是一定成功 的。若Cancel階段真的出錯了,需引入重試機制或人工處理。

二、TM 事務管理器

TM事務管理器可以實作為獨立的服務,也可以讓全局事務發起方充當 TM 的角色,TM 獨立出來是為了成為公 用元件,是為了考慮系統結構和軟體複用。

TM 在發起全局事務時生成全局事務記錄,全局事務 ID 貫穿整個分布式事務調用鍊條,用來記錄事務上下文, 追蹤和記錄狀态,由于 Confirm 和 Cancel 失敗需進行重試,是以需要實作為幂等,幂等性是指同一個操作無論請求多少次,其結果都相同。

三、TCC解決方案

架構名稱 GitHub位址
tcc-transaction https://github.com/changmingxie/tcc-transaction
Hmily https://github.com/yu199195/hmily
ByteTCC https://github.com/liuyangming/ByteTCC
EasyTransaction https://github.com/QNJR-GROUP/EasyTransaction

四、TCC需要注意的問題

1、空復原

在沒有調用 TCC 資源 Try 方法的情況下,調用了二階段的 Cancel 方法,Cancel 方法需要 識别出這是一個空復原 ,然後直接傳回成功。

出現原因:是當一個分支事務所在服務當機或網絡異常,分支事務調用記錄為失敗,這個時候其實是沒有執行Try階段,當故障恢複後,分布式事務進行復原則會調用二階段的Cancel方法,進而形成空復原。

解決方法:識别出這個空復原。需要知道一階段是否執行,如果執行了,那就是正常復原;如果沒執行,那就是空復原。前面已經說過TM在發起全局事務時生成全局事務記錄,全局事務ID貫穿整個分布式事務調用鍊條。再額外增加一張分支事務記錄表,其中有全局事務 ID 和分支事務 ID,第一階段 Try 方法裡會插入一條記錄,表示一階段執行了。

//在cancel中cancel空復原處理,如果try沒有執行,cancel不允許執行
if(accountInfoDao.isExistTry(transId)<=0){
    log.info("bank1 空復原處理,try沒有執行,不允許cancel執行,xid:{}",transId);
    return ;
}
           

2、幂等

為了保證TCC二階段送出重試機制不會引發資料不一緻,要求 TCC 的二階段 Try、Confirm 和 Cancel 接口保證幂等,這樣不會重複使用或者釋放資源。如果幂等控制沒有做好,很有可能導緻資料不一緻等嚴重問題。

3、懸挂

懸挂就是對于一個分布式事務,其二階段 Cancel 接口比 Try 接口先執行。

出現原因:RPC 調用分支事務try時,先注冊分支事務,再執行RPC調用,如果此時 RPC 調用的網絡發生擁堵,通常 RPC 調用是有逾時時間的, RPC 逾時 以後,TM就會通知RM 復原 該分布式事務,可能復原完,RPC 請求才到達參與者真正執行,而一個 Try 方法預留的業務資源。

解決思路:如果二階段執行完成,那一階段就不能再繼續執行。在執行一階段事務時判斷在該全局事務下, “分支事務記錄”表中是否已經有二階段事務記錄 ,如果有則不執行Try。

舉例,場景為 A 轉賬 30 元給 B,A 和 B 賬戶在不同的服務。

方案:

賬戶 A

try:
    檢查餘額是否夠30元
    扣減30元
 
confirm:
    空
 
cancel:
    增加30元
           

賬戶 B

try:
    增加30元
 
confirm:
    空
 
cancel:
    減少30元
           

方案說明

(1)賬戶 A,這裡的餘額就是所謂的業務資源,按照前面提到的原則,在第一階段需要檢查并預留業務資源,是以,我們在扣錢 TCC 資源的 Try 接口裡先檢查 A 賬戶餘額是否足夠,如果足夠則扣除 30 元。 Confirm 接口表示正式送出,由于業務資源已經在 Try 接口裡扣除掉了,那麼在第二階段的 Confirm 接口裡可以什麼都不用做。Cancel 接口的執行表示整個事務復原,賬戶A復原則需要把 Try 接口裡扣除掉的 30 元還給賬戶。

(2)賬号B,在第一階段 Try 接口裡實作給賬戶 B 加錢,Cancel 接口的執行表示整個事務復原,賬戶 B 復原則需要把 Try 接口裡加的 30 元再減去。

方案問題分析

1、如果賬戶 A 的 Try 沒有執行在 Cancel 則就多加了 30 元。

2、由于 Try、Cancel、Confirm 都是由單獨的線程去調用,且會出現重複調用,是以都需要實作幂等。

由于 Try、Cancel、Confirm 都是由單獨的線程去調用,且會出現重複調用,是以都需要實作幂等。

3、賬号 B 在 Try 中增加 30 元,當 Try 執行完成後可能會其它線程給消費了。

4、如果賬戶 B 的 Try 沒有執行在 Cancel 則就多減了 30 元。

問題解決:

1、賬戶 A 的 Cancel 方法需要判斷 Try 方法是否執行,正常執行 Try 後方可執行 Cancel。

2、Try、Cancel、Confirm方法實作幂等。

3、賬号 B 在 Try 方法中不允許更新賬戶金額,在 Confirm 中更新賬戶金額。

4、賬戶 B 的 Cancel 方法需要判斷 Try 方法是否執行,正常執行 Try 後方可執行 Cancel。

優化方案:

賬戶 A

try:
    try幂等校驗
    try懸挂處理
    檢查餘額是否夠30元
    扣減30元
 
confirm:
    空
 
cancel:
    cancel幂等校驗
    cancel空復原處理
    增加可用餘額30元
           

賬戶 B

try:
    空
confirm:
    confirm幂等校驗
    正式增加30元
cancel:
    空
           

總結:

如果拿TCC事務的處理流程與2PC兩階段送出做比較, 2PC通常都是在跨庫的DB層面 ,而 TCC則在應用層面的處理 ,需要通過業務邏輯來實作。這種分布式事務的實作方式的 優勢 在于,可以讓應用自己定義資料操作的粒度,使得 降低鎖沖突、提高吞吐量 成為可能。

而 不足之處 則在于對應用的 侵入性非常強 ,業務邏輯的每個分支都需要實作try、confirm、cancel三個操作。此外,其 實作難度也比較大 ,需要按照網絡狀态、系統故障等不同的失敗原因實作不同的復原政策。

繼續閱讀