TCC真沒這麼簡單,一文講透|分散式事務系列(三)
來源:後端開發技術
本文從兩個場景說起,詳細描述了TCC的詳細過程,以及對比2PC有什麼區別,適用什麼樣的場景。
在面試前複習 TCC 的時候你是不是這樣做的:百度TCC關鍵詞,隨便找了篇文章,查詢到他有try、confirm、Cancel 三個階段,業務侵入度高,和兩階段差不多。複習完畢。
如果你是這樣去理解和複習的,只能說對 TCC 的理解太不到位了,真的有必要耐心看完這篇文章。
TCC
有些人可能覺得 TCC 和兩階段提交非常相似,無法區分。首先強調,TCC是一種最終一致性方案,他並不是強一致性。如果你不瞭解兩階段提交,請先看這篇。
分散式事務,強一致性方案有哪些?|分散式事務系列(二)
這篇文章我們來探討下 TCC 到底是什麼,他用來解決什麼問題?在繼續閱讀之前,我先丟擲兩個問題。
一個面試中的問題
在某大廠面試的時候,面試官問了我一個問題:由於我介紹的是一個支付專案,業務系統呼叫支付系統扣款成功後採用本地事務狀態表(最終一致性)的方案通知業務系統。由於我們的系統QPS並不高,所以我自認為這樣的方案是沒問題的。
但是面試官問我,如果扣款成功後訂單系統資料庫寫入不可用怎麼辦?雖然使用者支付成功,但是即使有短暫幾分鐘的寫入不可用也會導致客訴暴漲,你怎麼解決這個問題?
這讓我意識到,如果在高併發場景下這個方案是有問題的。本地事務狀態表的缺點就是及時性不夠,當然這個可以透過再本地事務結束後及時發起對業務的通知解決。但是有個問題無法解決,本地事務狀態表都是基於本地業務執行成功後,下游依賴業務也會成功。如果下游系統發生不可用問題,我們的本地事務狀態表中狀態將阻塞在這裡,出現上述支付扣款後一直無法通知到訂單的情況。
電商超賣問題
除了上述面試問題,還有一個電商中很常見的超賣問題。
還是以電商場景下的餘額支付、扣庫存為例。如果我們採用可靠訊息佇列或者本地事務表的方案,使用者購買了一瓶可樂,餘額扣款成功後傳送訊息到庫存系統,庫存系統對可樂的庫存減1。如果是隻有一個使用者買並且庫存充足的情況下這是沒有問題的,但是如果此時有3個使用者在購買可樂,但是庫存僅剩 1 瓶,支付前使用介面檢查剩餘庫存的時候是透過的。但是等到支付成功,扣減庫存的訊息同步到庫存服務的時候系統就會出現超賣 2 瓶的情況。
之所以出現上述問題,是因為建立訂單在業務上是使用者之間隔離的,業務上不會有資源的競爭,但是庫存資料是一種共享資源,多個使用者同時購買同樣的商品就會出現併發問題,所以需要不同的執行緒之間事務隔離。
實現隔離性可以使用我們之前提到的強一致性方案 兩階段提交,但是在電商場景高併發下,兩階段提交持有資源時間過長並不合適。強一致性效能太低,訊息佇列方案由無法資源隔離,怎麼辦呢?TCC 方案就是為這種場景而生的。
什麼是 TCC
TCC 是“Try-Confirm-Cancel”三個單詞的縮寫,是由資料庫專家 Pat Helland 在 2007 年撰寫的論文《Life beyond Distributed Transactions: An Apostate’s Opinion》中提出。一提到 TCC 我們都知道它是一種業務侵入較強的分散式事務方案,要求業務處理過程必須拆分為“try ”和“ confirm/cancel”兩個階段,並且需要分別提供這兩個階段所涉及三個步驟的介面。
Try :嘗試執行階段,完成所有業務可執行性的檢查(保障一致性),並且預留好全部需用到的業務資源(保障隔離性)。 Confirm :確認執行階段,不進行任何業務檢查,直接使用 Try 階段準備的資源來完成業務處理(也就是說業務上理論一定可以執行成功)。由於分散式環境下的不可靠性,Confirm 階段可能會重複執行,因此本階段所執行的操作需要具備冪等性。 Cancel:取消執行階段,釋放 Try 階段預留的業務資源。由於分散式環境下的不可靠性,Cancel 階段可能會重複執行,也需要滿足冪等性。
具體時序圖如下:
這三個階段的特點如下:
流程 | 特點 |
---|---|
Try | 預留業務資源(不鎖定資源,獨立事務) |
Confirm | 確認提交資源(需要重試,最終一致性) |
Cancel | 釋放資源(需要重試,最終一致性) |
如何解決前文兩個問題
訂單狀態不更新問題
第一個問題,如何解決扣款成功訂單狀態遲遲不更新:
如果採用TCC方案,首先本地建立事務,生成事務 ID,記錄在活動日誌中,此時狀態為Try。 訂單在扣款和通知訂單狀態變更會在Try階段進行檢查,首先在使用者的餘額系統鎖定訂單金額,然後通知訂單狀態變為支付待確認。 如果訂單庫此時寫入不可用,那麼Try階段訂單服務會失敗,活動日誌狀態變更為 Cancel。 進入Cancel階段後,餘額服務將鎖定的金額釋放,通知訂單支付不成功,如果任意環節失敗,可以用最終一致性方案不斷嘗試。
這樣,上述問題透過餘額回退的方式得到化解。
超賣問題
第二個問題,如何解決電商超賣的問題:
使用者發起支付請求,購買一瓶可樂。
建立事務,生成事務 ID,記錄在活動日誌中,進入 Try 階段:
餘額服務嘗試鎖定金額,庫存服務嘗試鎖定庫存資源。兩者有任意失敗或者介面超時活動日誌的狀態記錄為 Cancel,將進入 Cancel階段;全部成功則進入 Confirm 階段,活動日誌的狀態記錄為 Confirm。
Confirm 階段,說明Try全部成功:餘額服務執行扣減金額,庫存執行扣減庫存。
Confirm 階段如果全部完成,事務正常結束。如果任何一個環節出現異常,不論是業務異常或者網路異常,都將根據活動日誌中的記錄,重複執行該服務的 Confirm 操作,實現最終一致。
Cancel 階段,說明Try 階段有服務超時或者執行失敗:撤銷使用者被鎖定的金額,解鎖被佔用的庫存。
Cancel 階段如果全部完成,事務以執行回滾結束。如果在 Cancel 時任意服務超時或者失敗,都將根據活動日誌中的記錄,重複執行該服務的 Cancel 操作,實現最終一致性。
假設可樂只剩 1 瓶,因為我們在第 Try 階段首先執行餘額鎖定和庫存扣減,如果使用者A扣款成功,並且鎖定庫存成功,此時可樂的可購買庫存為0。當使用者B併發購買,即使餘額鎖定成功,但是檢查庫存時發現已經庫存不足,將通知餘額解鎖金額,超賣問題由此解決。
TCC與2PC對比
正因為 TCC 也分為了 Try 和 Confirm/Cancel 兩個階段,所以很多人對此和兩階段提交產生了混淆,這裡用兩個表格列出他們的異同。
相同:
TCC | 兩階段提交 |
---|---|
可分為 Try 和 Confirm/Cancel 兩個階段 | 可以分為投票階段和提交階段 兩個階段 |
在Try階段佔用資源 | 在第一階段佔用資源 |
不同:
TCC | 兩階段提交 |
---|---|
實現了最終一致性 | 剛性事務,實現了強一致性 |
服務的程式碼邏輯層面實現,需要對三種操作分別編碼,有業務侵入,開發成本更高 | 底層資料庫支援兩階段協議,無業務侵入 |
第一階段,Try 階段會直接提交事務,只會短暫持有資源鎖,效能較高 | 第一階段會持續鎖定資源並持有鎖,事務不提交,造成服務阻塞,效能低 |
第二階段的 Confirm 和 Cancel 如果出現失敗,會利用最終一致性方案不斷重試 | 第二階段如果有失敗,會造成系統之間的資料不一致 |
TCC 的每個步驟在資料庫層面都是一個獨立事務 | 兩階段的兩個步驟和起來是一個完整的事務 |
適用場景
說了這麼多,我們應該在什麼場景下使用 TCC 呢?我做了些總結。
當常規最終一致性方案無法滿足,需要更強的一致性。 當業務對實時性要求高。 當業務涉及到資源爭奪,需要更高的隔離性。 當業務在滿足一致性和隔離性的時候,還需要更好的效能表現。
當你的業務遇到這些問題的時候,那就可以考慮TCC了,比如涉及到資金資料,銀行業務,金融業務,涉及到交易、支付、賬務都可以考慮。
但是上述優點都是有代價的,TCC 對於業務的侵入性非常強,業務邏輯的每個分支都需要實現try、confirm、cancel三個操作。難度也比較大,需要根據不同的失敗原因實現不同的回滾策略,有更高的開發成本和更換事務實現方案的替換成本。我們可以基於某些分散式事務中介軟體(譬如阿里開源的Seata)去完成,儘量減輕一些編碼工作量。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2942604/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分散式事務(四)之TCC分散式
- 分散式事務(2)---TCC理論分散式
- 第一次有人把“分散式事務”講的這麼簡單明瞭分散式
- Golang 實現 Redis(8): TCC分散式事務GolangRedis分散式
- 分散式事務解決方案(五)【TCC型方案】分散式
- Seata 分散式事務框架 TCC 模式原始碼分析分散式框架模式原始碼
- php基於dtm分散式事務管理器實現tcc模式分散式事務demoPHP分散式模式
- 分散式事務 TCC-Transaction 原始碼解析 —— 事務儲存器分散式原始碼
- Feacar分散式事務框架簡單使用分散式框架
- 分散式事務 TCC-Transaction 原始碼分析 —— Dubbo 支援分散式原始碼
- 一文看懂分散式事務分散式
- 分散式系統(三)——分散式事務分散式
- 分散式系列七: 分散式事務理論分散式
- 一文講透 Redis 事務 (事務模式 VS Lua 指令碼)Redis模式指令碼
- 微服務痛點-基於Dubbo + Seata的分散式事務(TCC模式)微服務分散式模式
- 分散式事務,原來可以這麼玩?分散式
- TCC和兩階段分散式事務處理的區別分散式
- 分散式事務 Seata TCC 模式深度解析 | SOFAChannel#4 直播整理分散式模式
- 分散式事務 TCC-Transaction 原始碼分析 —— 運維平臺分散式原始碼運維
- seata分散式事務TCC模式介紹及推薦實踐分散式模式
- TXC分散式事務簡介分散式
- 分散式柔性事務的TCC方案分散式
- 如何實現一個TCC分散式事務框架的一點思考分散式框架
- 分散式事務 TCC-Transaction 原始碼分析 —— 除錯環境搭建分散式原始碼除錯
- 分散式事務(4)---最終一致性方案之TCC分散式
- 用python輕鬆完成一個分散式事務TCC,保姆級教程Python分散式
- 用Go輕鬆完成一個分散式事務TCC,保姆級教程Go分散式
- 用Java輕鬆完成一個分散式事務TCC,保姆級教程Java分散式
- 用PHP輕鬆完成一個分散式事務TCC,保姆級教程PHP分散式
- 透徹,分散式事務一網打盡分散式
- 分散式事務,強一致性方案有哪些?|分散式事務系列(二)分散式
- 分散式事務(一)—分散式事務的概念分散式
- 分散式系列七: zookeeper簡單用法分散式
- 分散式事務(七)之Seata簡介分散式
- lms框架分散式事務使用簡介框架分散式
- 微服務分散式事務之LCN、TCC特點、事務補償機制緣由以及設計重點微服務分散式
- SpringCloud系列——TX-LCN分散式事務管理SpringGCCloud分散式
- 分散式事務(3)---RocketMQ實現分散式事務原理分散式MQ