各種分散式事務的實現方式適用的場景

njutbkk發表於2019-02-24

一、事務和分散式事務

單機事務我們經常看到,分散式事務,通俗點將,就是需要在各個機器上跑的事務,但是事務的每一步都不知道其他步是否成功,但是在業務上又要保證,所有的步驟,要麼都成功,要麼都不成功。

分散式事務實現的方式(乾貨

  1. 基於訊息實現的分散式事務
  2. 基於補償實現的分散式事務(gts/fescar自動補償的形式)
  3. 基於TCC實現的分散式事務
  4. 基於SAGA實現的分散式事務
  5. 基於2PC實現的分散式事務

何時使用單機事務

這個相信大家都很清楚,在條件允許的情況下,我們應該儘可能地使用單機事務,因為單機事務裡,無需額外協調其他資料來源,減少了網路互動時間消耗以及協調時所需的儲存IO消耗,在修改等量業務資料的情況下,單機事務將會有更高的效能。

何時選擇基於訊息實現的事務?

基於訊息實現的事務適用於分散式事務的提交或回滾只取決於事務發起方的業務需求,其他資料來源的資料變更跟隨發起方進行的業務場景。

舉個例子,假設存在業務規則:某筆訂單成功後,為使用者加一定的積分。

在這條規則裡,管理訂單資料來源的服務為事務發起方,管理積分資料來源的服務為事務跟隨者。

從這個過程可以看到,基於訊息佇列實現的事務存在以下操作:

訂單服務建立訂單,提交本地事務 訂單服務釋出一條訊息 積分服務收到訊息後加積分 我們可以看到它的整體流程是比較簡單的,同時業務開發工作量也不大:

編寫訂單服務裡訂單建立的邏輯 編寫積分服務裡增加積分的邏輯 可以看到該事務形態過程簡單,效能消耗小,發起方與跟隨方之間的流量峰谷可以使用佇列填平,同時業務開發工作量也基本與單機事務沒有差別,都不需要編寫反向的業務邏輯過程。因此基於訊息佇列實現的事務是我們除了單機事務外最優先考慮使用的形態。

但是如何保證訊息的可靠性,是需要我們處理的。 大家可以看一下這篇文章,: 【spring cloud實現可靠訊息一致性】。這位兄弟已經寫的很詳細了,我就不細說了

何時選擇利用補償實現的事務?

但是基於訊息實現的事務並不能解決所有的業務場景,例如以下場景:某筆訂單完成時,同時扣掉使用者的現金

這裡事務發起方是管理訂單庫的服務,但對整個事務是否提交併不能只由訂單服務決定,因為還要確保使用者有足夠的錢,才能完成這筆交易,而這個資訊在管理現金的服務裡。這裡我們可以引入基於補償實現的事務,其流程如下:

建立訂單資料,但暫不提交本地事務 訂單服務傳送遠端呼叫到現金服務,以扣除對應的金額 上述步驟成功後提交訂單庫的事務 以上這個是正常成功的流程,異常流程需要回滾的話,將額外傳送遠端呼叫到現金服務以加上之前扣掉的金額。

以上流程比基於訊息佇列實現的事務的流程要複雜,同時開發的工作量也更多:

編寫訂單服務裡建立訂單的邏輯 編寫現金服務里扣錢的邏輯 編寫現金服務裡補償返還的邏輯 可以看到,該事務流程相對於基於訊息實現的分散式事務更為複雜,需要額外開發相關的業務回滾方法,也失去了服務間流量削峰填谷的功能。但其僅僅只比基於訊息的事務複雜多一點,若不能使用基於訊息佇列的最終一致性事務,那麼可以優先考慮使用基於補償的事務形態。

阿里GTS/fescar本質上也是這補償的程式設計模型,只不過補償程式碼自動生成,無需業務干預,同時接管應用資料來源,禁止業務修改處於全域性事務狀態中的記錄。因此,其關於讀場景的適用性,可參考補償。但其在寫的適用場景由於引入了全域性事務時的寫鎖,其寫適用性介於 TCC以及補償之間 。

何時選擇利用TCC實現的事務

然而基於補償的事務形態也並非能實現所有的需求,如以下場景:某筆訂單完成時,同時扣掉使用者的現金,但交易未完成,也未被取消時,不能讓客戶看到錢變少了。

這時我們可以引入TCC,其流程如下:

訂單服務建立訂單 訂單服務傳送遠端呼叫到現金服務,凍結客戶的現金 提交訂單服務資料 訂單服務傳送遠端呼叫到現金服務,扣除客戶凍結的現金 以上是正常完成的流程,若為異常流程,則需要傳送遠端呼叫請求到現金服務,撤銷凍結的金額。

以上流程比基於補償實現的事務的流程要複雜,同時開發的工作量也更多:

訂單服務編寫建立訂單的邏輯 現金服務編寫凍結現金的邏輯 現金服務編寫扣除現金的邏輯 現金服務編寫解凍現金的邏輯 TCC實際上是最為複雜的一種情況,其能處理所有的業務場景,但無論出於效能上的考慮,還是開發複雜度上的考慮,都應該儘量避免該類事務。 所以我們開發了TCC框架,來對應大多數的業務場景,下一篇寫一下TCC

何時選擇利用SAGA實現的事務?

SAGA可以看做一個非同步的、利用佇列實現的補償事務。

其適用於無需馬上返回業務發起方最終狀態的場景,例如:你的請求已提交,請稍後查詢或留意通知 之類。

將上述補償事務的場景用SAGA改寫,其流程如下:

訂單服務建立最終狀態未知的訂單記錄,並提交事務 現金服務扣除所需的金額,並提交事務 訂單服務更新訂單狀態為成功,並提交事務 以上為成功的流程,若現金服務扣除金額失敗,那麼,最後一步訂單服務將會更新訂單狀態為失敗。

其業務編碼工作量比補償事務多一點,包括以下內容:

訂單服務建立初始訂單的邏輯 訂單服務確認訂單成功的邏輯 訂單服務確認訂單失敗的邏輯 現金服務扣除現金的邏輯 現金服務補償返回現金的邏輯 但其相對於補償事務形態有效能上的優勢,所有的本地子事務執行過程中,都無需等待其呼叫的子事務執行,減少了加鎖的時間,這在事務流程較多較長的業務中效能優勢更為明顯。同時,其利用佇列進行進行通訊,具有削峰填谷的作用。

因此該形式適用於不需要同步返回發起方執行最終結果、可以進行補償、對效能要求較高、不介意額外編碼的業務場景。

但當然SAGA也可以進行稍微改造,變成與TCC類似、可以進行資源預留的形態。

2PC事務

其適用於參與者較少,單個本地事務執行時間較少,並且參與者自身可用性很高的場景,否則,其很可能導致效能下降嚴重。

並非一種事務形態就能打遍天下 通過分析我們可以發現,並不存在一種事務形態能解決所有的問題,我們需要根據特定的業務場景選擇合適的事務形態。甚至於有時需要混合多種事務形態才能更好的完成目標,如 上面提到的 訂單、積分、錢包混合的場景:訂單的成功與否需要依賴於錢包的餘額,但不依賴於積分的多少,因此可以混合基於訊息的事務形態以加積分 及 基於補償的事務形態以確保扣錢成功,從而得到一個效能更好,編碼量更少的形態。

然而目前很多框架都專注於某單一方面的事務形態,如TCC單獨一個框架,可靠訊息單獨一個框架,SAGA單獨一個框架,他們各自獨立,容易導致以下問題:

由於前期只採用了其中一種型別事務的框架,因為工具目前只有錘子,引入其他工具又涉及測試、閱讀程式碼等過程,因此把所有問題都看做釘子,導致效能偏低或者實現不夠優雅 由於不同框架管理事務的形態可能不一致,導致不能很好的協調工作,如某一個TCC框架和另一個基於訊息的事務框架無法很好融合。

總結

不同業務場景應按需引入不同的事務形態。

相關文章