TCC
在前一篇文章中講到了BASE模式,這種模式可以應用在單庫or跨庫事務的場景下。事實上BASE模式不僅僅侷限於數據庫層面,還可以應用於分佈式系統,這類分佈式系統最典型的例子就是電商平臺,它們有以下幾個特徵:
- SOA化/微服務化:單體應用拆分成多個服務。
- 數據庫的各種拆分技術的運用:分表、分庫、分區。
- 大量NoSQL數據庫的興起。
- 應用之間的通信手段並非直接讀數據庫:RESTful、RPC、消息中間件等。
- 大量跨應用事務的出現。
在這種場景下,2PC(及XA)已經無法滿足需求,因爲它:
- 性能低下,2PC協議是阻塞式的。當協調的數據庫越來越多時,性能無法接受。
- 無法水平擴展以提升性能,只能靠垂直擴展(提升硬件)——更快的CPU、更快更大的硬盤、更大更快的內存——但是這樣很貴,並且很容易遇到極限。
- 染指其他數據庫。
- 依賴於數據庫是否支持2PC(XA)。
而BASE只解決最後提交的問題,不能解決諸如在上一篇文章中最後提到的如何保證刷卡消費不透支的問題.於是就有人提出了TCC模式(Try、Confirm、Cancel),這一模式在國內因阿里巴巴的推廣而廣爲人知。
協議介紹
在TCC協議裏,參與的主體分爲兩種:
- 發起方:發起事務的應用。
- 參與方:執行事務請求,手上握有資源的服務。
並且有三種動作:Try、Confirm、Cancel。
TCC是Try、Confirm、Cancel的簡稱,它們分別的職責是:
- Try:負責預留資源(比如新建一條狀態=PENDING的訂單),同時也做業務檢查(比如看看餘額是否足夠),簡單來說就是不能預留已經被佔用的資源。
- Confirm:負責落地所預留的資源(比如扣費、把訂單狀態變成COMPLETED)
- Cancel:負責撤銷所預留的資源(比如把訂單狀態變成CANCELED)
關於預留資源要多說兩句,資源都是有限的,因此預留資源都是有時效的,如果當預留資源遲遲得不到Confirm——我們將這種情況稱爲timeout——參與方會自行將其Cancel(這裏有坑,下面會講)。也就是說參與方對於資源具有自我管理能力,這樣可以避免因發起方的問題導致資源被長期佔用。
TCC於BASE相比,增加了業務檢查和撤銷事務的功能。
同時,TCC將2PC數據庫層面的動作提升到了服務層面,不同的是TCC的所有動作都是一個本地事務,每個本地事務都在動作完成後commit到數據庫:
- Try相當於2PC的Commit request phase,外加了業務檢查邏輯
- Confirm相當於2PC的Commit phase的commit動作
- Cancel相當於2PC的Commit phase的rollback動作
流程
以下是TCC的狀態圖:
下面是流程步驟(你會發現和2PC很像):
- 【發起方】發送Try到所有參與方
- 每個【參與方】執行Try,預留資源
- 【發起方】收到所有【參與方】的Try結果
- 【發起方】發送Confirm/Cancel到所有參與房
- 每個【參與方】執行Confirm/Cancel
- 【發起方】收到所有【參與方】的Confirm/Cancel結果
異常處理
從【發起方】的角度來看出現異常要怎麼處理:
- step 1發生異常,【發起方】可以什麼都不做等待【參與方】自行Cancel,也可以直接發起Cancel請求
- step 2、3發生異常,意味着【發起方】沒有收到【參與方】的響應,這個時候因認定爲失敗,執行Cancel
- step 4發生異常,【發起方】重試Confirm/Cancel
- step 5、6發生異常,意味着【發起方】沒有收到【參與方】的響應,這個時候因認定爲失敗,重試Confirm/Cancel
從【參與方】角度來看看看出現異常怎麼處理:
- step 1,意味着【參與方】沒有收到請求,什麼都不需要做
- step 2,意味着【參與方】沒有執行成功,什麼都不需要做
- step 3,意味着【發起方】沒有收到結果,什麼都不需要做,等待【發起方】重試即可。【參與方】要保證prepare是冪等的。
- step 4,等待【發起方】重試,或者等待timeout後自行Cancel。
- step 5,等待【發起方】重試即可
- step 6,意味着【發起方】沒有收到結果,什麼都不需要做,等待【發起方】重試即可,【參與方】要保證Confirm/Cancel是冪等的。
觀察一下你就會發現TCC和2PC存在一樣的問題:
- 若【發起方】/【參與方】因崩潰遺失了信息,則會造成有的【參與方】已Confirm,有的【參與方】則被Cancel了,甚至於依然保持在預留狀態。
- 若【發起方】在step 4發送Confirm,而【參與方】在Cancel(因timeout導致)。
不過TCC在處理這種情況相比2PC具有一些優勢,因爲TCC是在服務層面的,當出現這種問題的時候可以很容易通過日誌、業務數據排查出來,然後人工介入,而2PC完全是數據庫底層的。
對於ACID的保證
TCC對於ACID的保證:
- A,正常情況下保證
- C,在某個時間點,會出現A庫和B庫的數據違反一致性要求的情況,但是最終是一致的
- I,在某個時間點,A事務能夠讀到B事務部分提交的結果
- D,和本地事務一樣,只要commit則數據被持久
實現TCC時的注意事項
實現TCC需要關注以下幾個方面:
- TCC模式在於服務層面而非數據庫層面
- TCC模式依賴於各服務正確實現Try、Confirm、Cancel和timeout處理機制
- TCC模式最少通信次數爲2n(n=服務數量)
- 不是所有業務模型都適合使用TCC,比如發郵件業務根本就不需要預留資源
- 需要良好地設計服務的日誌、人工處理流程/機制,便於異常情況的處理