03.21 阿里P8技術專家從理論到實踐幫你解析分佈式事務

Best Effort

best effort即盡最大努力交付,主要用於在這樣一種場景:

不同的服務平臺之間的事務性保證。比如我們在電商購物,使用支付寶支付;又比如玩網遊的時候,通過App Store充值。拿購物為例,電商平臺與支付平臺是相互獨立的,隸屬於不同的公司,即使是同一個公司也很可能是獨立的部門。因此,這兩個平臺是不可能使用同一套分佈式事務框架的,2PC不行,tcc也不行,異步消息也不行。

其實在上面電商平臺與支付平臺的例子中,涉及到多重事務性:

電商平臺與支付平臺之間的事務性:電商的下單操作與支付平臺扣款的原子性,不能說支付平臺扣了用戶的錢,但電商平臺不發貨;或者說,電商平臺先發了貨,支付平臺沒有扣用戶的錢;

電商平臺內部的事務性:比如訂單與優惠券、紅包等;

支付平臺內部的事務性:比如用戶賬戶、商戶賬戶等;

不管是因為技術原因,還是說安全策略,支付平臺只會提供給電商平臺一些Http接口,即開放支付服務。電商平臺在發出一筆支付請求後,是不大可能立刻獲得支付是成功還是失敗的確切消息,更多的時候應該是請求已被接受,處理中。這個時候支付平臺已經將該請求持久化,保證一定會處理這個請求。當支付平臺處理完這個支付請求之後,怎麼將結果通知給電商平臺呢,要麼是電商平臺定時輪訓,要麼是電商平臺在初始支付請求的時候攜帶一個callback,提供給支付平臺回調。在這篇文章中提到,支付寶採用的是回調的形式:

“做過支付寶交易接口的同學都知道,我們一般會在支付寶的回調頁面和接口裡,解密參數,然後調用系統中更新交易狀態相關的服務,將訂單更新為付款成功。同時,只有當我們回調頁面中輸出了success字樣或者標識業務處理成功相應狀態碼時,支付寶才會停止回調請求。否則,支付寶會每間隔一段時間後,再向客戶方發起回調請求,直到輸出成功標識為止。”

這個例子,繪製成流程圖就是這樣樣子的:

阿里P8技術專家從理論到實踐幫你解析分佈式事務

再想想上文提到的銀行轉賬的例子,很可能也是採用best effort這種模式,銀行之間肯定是相互獨立的。首先是本地銀行先扣款,然後通知另外一個銀行加款,但為什麼對方加款失敗,沒有通知到本地銀行,就不清楚了

分佈式事務解決方案比較

在這裡主要通過以下幾個維度來對比分析:

  • 一致性

  • 資源鎖粒度:是否要利用到數據庫的鎖機制,加鎖的粒度

  • 子事務串並行:組成 一個事務的多個子事務是併發執行,還是串行執行

  • 回滾(補償):是哪個層面的回滾(補償)、回滾的代價

注意,上面提到的回滾和補償是一個意思,“回滾”不侷限於DB裡面的術語,而是指通用的對某個操作的逆反操作

阿里P8技術專家從理論到實踐幫你解析分佈式事務

2PC的強一致性依賴於數據庫,而TCC的強一致性依賴於應用層的Commit與cancel。異步消息,1PC,best effort都只保證最終一致性(且最終一致性還可能依賴於人工介入,是否應該算弱一致性?)

  2PC需要對整個資源加鎖,因此不適用於高併發的分佈式場景;而tcc只對需要的資源進行加鎖,加鎖的粒度小,且try commit Cancel都是本地短事務,因此能在保證強一致性的同時最大化提高系統可用性。而異步消息,1PC,best effort都是先提交一部分事務,無需加鎖。

2PC是有數據庫來保證回滾,而TCC是應用層實現回滾:為每一個try操作提供一個對應的cancel操作。而異步消息,1PC適用於理論上一定會成功的場景,難以回滾。best effort這種模式,需要服務的調用者實現完整的一個事務操作用於回滾,比如支付失敗的情況。數據庫的回滾較簡單,而應用層的回滾較為困難,更重要的是,回滾也需要作為一個事務進行,部分回滾失敗的情況最可怕。

至於子事務的串行、並行,在其他文章中並沒有看見過相關討論,但肯定是實踐的時候必須要考慮的問題。即一個分佈式事務肯定是由多個分支事務組成,那麼多個分支事務是併發執行,還是串行執行呢?特別對於2PC,TCC這些分為多個階段的解決方案,每個階段是併發,還是串行呢

分支事務串並行與LPO

  首先,對於異步消息,best effort,肯定都是串行的,其中一個分支事務完成之後,再去做另一個分支事務。

  但對於2PC,TCC,理論上看起來是並行的,但工程實踐中有可以串行。以2PC為例

2PC從介紹的文章來看,多屬於並行:即協調者同時讓參與者prepare,然後在第二階段同時通知參與者commit或者abort,下面兩個圖說明了這個並行的過程。

阿里P8技術專家從理論到實踐幫你解析分佈式事務

阿里P8技術專家從理論到實踐幫你解析分佈式事務

上面分別是兩階段提交協議成功commit與失敗abort的情況,可以看出在prepare階段,多個參與者是並行的。

而2PC的串行模式,就是說,先通知一個參與者準備,成功的話再通知另一個參與者準備,即準備階段是串行的。下圖來自支付寶:

阿里P8技術專家從理論到實踐幫你解析分佈式事務

阿里P8技術專家從理論到實踐幫你解析分佈式事務

注意 上面的圖示,第二階段(commit 或者 abort)也畫成串行的,這裡應該是可以並行的。

那麼串行、並行的區別在於哪裡呢

  (1)並行效率高,整個事務的耗時更少;

  (2)而串行在prepare階段失敗的情況下,只需部分回滾;

在工程實踐中為什麼會採用串行這種方式呢,這是另外一個重要的優化: “最末參與者優化”(Last Participant Optimization,術語來自支付寶),即允許兩階段提交協議中有一個參與者不實現“準備”操作,在其餘參與者都prepare ok的情況下,直接提交自己的分式事務。

  網絡上關於LPO的介紹並不多,在oracle官網Logging Last Resource Transaction Optimization中有如下介紹:

The LLR resource uses a local transaction for its transaction work. The WebLogic Server transaction manager prepares all other resources in the transaction and then determines the commit decision for the global transaction based on the outcome of the LLR resource’s local transaction.  

  最末參與者優化的原理如下圖所示:

阿里P8技術專家從理論到實踐幫你解析分佈式事務

阿里P8技術專家從理論到實踐幫你解析分佈式事務

本質上,LPO是將最後一個參與者的準備操作與提交/放棄操作合併成一個提交操作,這樣提高了分佈式事務的執行效率。也可以看到,要使用LPO,在prepare階段一定是串行的。

對於TCC,流程也是非常類似2PC,即在Try階段,也可以使用LPO,在去前面的一文中,給出了一個實例的詳細流程圖。

如果想學習Java工程化、高性能及分佈式的深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加Java進階群:582505643,群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

在一些業務場景,是無需單獨的協調者,即事務的發起者同時是組成事務的分支事務。比如支付寶的例子,業務服務和賬戶服務組成一個分佈式事務,在業務服務上發起事務請求,因此沒有單獨的協調者服務器,使用LPO也比較適合。

再論TCC

前面已經介紹過TCC的三個階段,Try負責預留資源,Commit提交預留的資源,Cancel“回滾”預留的資源。那麼某一個分支事務的Try操作是否可以直接做Commit所做的事情呢,即Try操作直接提交分支事務。在這種情況下,如果所有分支事務的Try階段都返回OK,那麼該分支事務的Commit就什麼都不用做,如果需要Cancel,那麼就實現回滾。

當然,我看到的更多形式,比如支付寶的XTS,都只是凍結資源:加額外的字段,表明有多少數量的資源處於特殊狀態。

我們以一個扣款操作作為分支事務,比如要從賬戶A扣除100元。如果Try階段直接執行事務,那麼就從A的賬戶上真正扣除了,而Cancel階段則加上100,看起來很容易;如果Try階段只是凍結,那麼就會複雜一些,一個可行的方案增加forzen字段的值,同時扣除賬戶。

但如果考慮加款操作作為分支事務,Try階段直接執行事務的話,很可能出現cancel階段錢不夠的情況(假設資金不能為負)

因此,個人覺得,TCC框架是不用關心具體形式的,業務只需向框架註冊這三個操作就行了,具體怎麼操作,完全取決於業務,能滿足業務的需求就行。

實踐案例

阿里P8技術專家從理論到實踐幫你解析分佈式事務

阿里P8技術專家從理論到實踐幫你解析分佈式事務

偶然在一個視頻網站上看到了分佈式事務的實戰視頻感覺總結的比較到位。文章中的很多觀點都是借鑑到視頻中的觀點。


分享到:


相關文章: