理解分散式事務

貝聊科技發表於2018-12-10
文章首發於51CTO技術棧公眾號
作者 陳彩華
文章轉載交流請聯絡 caison@aliyun.com
複製程式碼

這篇文章將介紹什麼是分散式事務,分散式事務解決什麼問題,對分散式事務實現的難點,解決思路,不同場景下方案的選擇,通過圖解的方式進行梳理、總結和比較。

相信耐心看完這篇文章,談到分散式事務,不再只是有“2PC”、“3PC”、“MQ的訊息事務”、“最終一致性”、“TCC”等這些知識碎片,而是能夠將知識連成一片,形成知識體系。

1 什麼是事務

介紹分散式事務之前,先介紹什麼是事務。

事務的具體定義

事務提供一種機制將一個活動涉及的所有操作納入到一個不可分割的執行單元,組成事務的所有操作只有在所有操作均能正常執行的情況下方能提交,只要其中任一操作執行失敗,都將導致整個事務的回滾。

簡單地說,事務提供一種“ 要麼什麼都不做,要麼做全套(All or Nothing)”機制。

事務

資料庫事務的ACID屬性

事務是基於資料進行操作,需要保證事務的資料通常儲存在資料庫中,所以介紹到事務,就不得不介紹資料庫事務的ACID特性,指資料庫事務正確執行的四個基本特性的縮寫。包含:

  • 原子性(Atomicity) 整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被 回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。 例如:銀行轉賬,從A賬戶轉100元至B賬戶,分為兩個步驟:

    • (1)從A賬戶取100元
    • (2)存入100元至B賬戶。 這兩步要麼一起完成,要麼一起不完成,如果只完成第一步,第二步失敗,錢會莫名其妙少了100元。
  • 一致性(Consistency) 在事務開始之前和事務結束以後,資料庫資料的一致性約束沒有被破壞。 例如:現有完整性約束A+B=100,如果一個事務改變了A,那麼必須得改變B,使得事務結束後依然滿足A+B=100,否則事務失敗。

  • 隔離性(Isolation) 資料庫允許多個併發事務同時對資料進行讀寫和修改的能力,如果一個事務要訪問的資料正在被另外一個事務修改,只要另外一個事務未提交,它所訪問的資料就不受未提交事務的影響。隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。 例如:現有有個交易是從A賬戶轉100元至B賬戶,在這個交易事務還未完成的情況下,如果此時B查詢自己的賬戶,是看不到新增加的100元的。

  • 永續性(Durability) 事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。

資料庫事務的ACID特性

簡單而言,ACID是從不同維度描述事務的特性:

  • 原子性 —— 事務操作的整體性
  • 一致性 —— 事務操作下資料的正確性
  • 隔離性 —— 事務併發操作下資料的正確性
  • 永續性 —— 事務對資料修改的可靠性

一個支援事務(Transaction)的資料庫,需要具有這4種特性,否則在事務過程當中無法保證資料的正確性,處理結果極可能達不到請求方的要求。

什麼時候使用資料庫事務

在介紹完事務基本概念之後,什麼時候該使用資料庫事務? 簡單而言,就是業務上有一組資料操作,需要如果其中有任何一個操作執行失敗,整組操作全部不執行並恢復到未執行狀態,要麼全部成功,要麼全部失敗。

在使用資料庫事務時需要注意,儘可能短的保持事務,修改多個不同表的資料的冗長事務會嚴重妨礙系統中的所有其他使用者,這很有可能導致一些效能問題。

2 什麼是分散式事務

介紹完事務相關基本概念之後,下面介紹分散式事務。

分散式產生背景與概念

隨著網際網路快速發展,微服務,SOA等服務架構模式正在被大規模的使用,現在分散式系統一般由多個獨立的子系統組成,多個子系統通過網路通訊互相協作配合完成各個功能。

有很多用例會跨多個子系統才能完成,比較典型的是電子商務網站的下單支付流程,至少會涉及交易系統和支付系統,而且這個過程中會涉及到事務的概念,即保證交易系統和支付系統的資料一致性,此處我們稱這種跨系統的事務為分散式事務,具體一點而言,分散式事務是指事務的參與者、支援事務的伺服器、資源伺服器以及事務管理器分別位於不同的分散式系統的不同節點之上。

舉個網際網路常用的交易業務為例:

理解分散式事務

上圖中包含了庫存和訂單兩個獨立的微服務,每個微服務維護了自己的資料庫。在交易系統的業務邏輯中,一個商品在下單之前需要先呼叫庫存服務,進行扣除庫存,再呼叫訂單服務,建立訂單記錄。

理解分散式事務

可以看到,如果多個資料庫之間的資料更新沒有保證事務,將會導致出現子系統資料不一致,業務出現問題。

分散式事務的難點

  • 事務的原子性 事務操作跨不同節點,當多個節點某一節點操作失敗時,需要保證多節點操作的**要麼什麼都不做,要麼做全套(All or Nothing)**的原子性。
  • 事務的一致性 當發生網路傳輸故障或者節點故障,節點間資料複製通道中斷,在進行事務操作時需要保證資料一致性,保證事務的任何操作都不會使得資料違反資料庫定義的約束、觸發器等規則。
  • 事務的隔離性 事務隔離性的本質就是如何正確多個併發事務的處理的讀寫衝突和寫寫衝突,因為在分散式事務控制中,可能會出現提交不同步的現象,這個時候就有可能出現“部分已經提交”的事務。此時併發應用訪問資料如果沒有加以控制,有可能出現“髒讀”問題。

3 分散式系統的一致性

前面介紹到的分散式事務的難點涉及的問題,最終影響是導致資料出現不一致,下面對分散式系統的一致性問題進行理論分析,後面將基於這些理論進行分散式方案的介紹。

可用性和一致性的衝突 —— CAP理論

CAP

CAP 定理又被稱作布魯爾定理,是加州大學的電腦科學家布魯爾在 2000 年提出的一個猜想。2002 年,麻省理工學院的賽斯·吉爾伯特和南希·林奇發表了布魯爾猜想的證明,使之成為分散式計算領域公認的一個定理。

布魯爾在提出CAP猜想時並沒有具體定義 Consistency、Availability、Partition Tolerance 這3個詞的含義,不同資料的具體定義也有差別,為了更好地解釋,下面選擇Robert Greiner的文章《CAP Theorem》作為參考基礎。

  • CAP理論的定義 在一個分散式系統(指互相連線並共享資料的節點的集合)中,當涉及讀寫操作時,只能保證一致性(Consistence)、可用性(Availability)、分割槽容錯性(PartitionTolerance)三者中的兩個,另外一個必須被犧牲。

Consistency、Availability、Partition Tolerance具體解釋如下:

  • C - Consistency 一致性

A read is guaranteed to return the most recent write for a given client. 對某個指定的客戶端來說,讀操作保證能夠返回最新的寫操作結果。

這裡並不是強調同一時刻擁有相同的資料,對於系統執行事務來說,在事務執行過程中,系統其實處於一個不一致的狀態,不同的節點的資料並不完全一致。

一致性強調客戶端讀操作能夠獲取最新的寫操作結果,是因為事務在執行過程中,客戶端是無法讀取到未提交的資料的,只有等到事務提交後,客戶端才能讀取到事務寫入的資料,而如果事務失敗則會進行回滾,客戶端也不會讀取到事務中間寫入的資料。

  • A - Availability 可用性

A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout). 非故障的節點在合理的時間內返回合理的響應(不是錯誤和超時的響應)。

這裡強調的是合理的響應,不能超時,不能出錯。注意並沒有說“正確”的結果,例如,應該返回 100 但實際上返回了 90,肯定是不正確的結果,但可以是一個合理的結果。

  • P - Partition Tolerance 分割槽容忍性

The system will continue to function when network partitions occur. 當出現網路分割槽後,系統能夠繼續“履行職責”。

這裡網路分割槽是指: 一個分散式系統裡面,節點組成的網路本來應該是連通的。然而可能因為一些故障(節點間網路連線斷開、節點當機),使得有些節點之間不連通了,整個網路就分成了幾塊區域,資料就散佈在了這些不連通的區域中。

一致性、可用性、分割槽容忍性的選擇

雖然 CAP 理論定義是三個要素中只能取兩個,但放到分散式環境下來思考,我們會發現必須選擇 P(分割槽容忍)要素,因為網路本身無法做到 100% 可靠,有可能出故障,所以分割槽是一個必然的現象。

如果我們選擇了 CA(一致性 + 可用性) 而放棄了 P(分割槽容忍性),那麼當發生分割槽現象時,為了保證 C(一致性),系統需要禁止寫入,當有寫入請求時,系統返回 error(例如,當前系統不允許寫入),這又和 A(可用性) 衝突了,因為 A(可用性)要求返回 no error 和 no timeout。

因此,分散式系統理論上不可能選擇 CA (一致性 + 可用性)架構,只能選擇 CP(一致性 + 分割槽容忍性) 或者 AP (可用性 + 分割槽容忍性)架構,在一致性和可用性做折中選擇

  • CP - Consistency + Partition Tolerance (一致性 + 分割槽容忍性)
    CP模型

如上圖所示,因為Node1節點和Node2節點連線中斷導致分割槽現象,Node1節點的資料已經更新到y,但是Node1 和 Node2 之間的複製通道中斷,資料 y 無法同步到 Node2,Node2 節點上的資料還是舊資料x。

這時客戶端C 訪問 Node2 時,Node2 需要返回 Error,提示客戶端 “系統現在發生了錯誤”,這種處理方式違 背了可用性(Availability)的要求,因此 CAP 三者只能滿足 CP。

  • AP - Availability + Partition Tolerance (可用性 + 分割槽容忍性)
    AP模型

同樣是Node2 節點上的資料還是舊資料x,這時客戶端C 訪問 Node2 時,Node2 將當前自己擁有的資料 x 返回給客戶端 了,而實際上當前最新的資料已經是 y 了,這就不滿足一致性(Consistency)的要求了,因此 CAP 三者只能滿足 AP。

注意:這裡 Node2 節點返回 x,雖然不是一個“正確”的結果,但是一個“合理”的結果,因為 x 是舊的資料,並不是一個錯亂的值,只是不是最新的資料。

值得補充的是,CAP理論告訴我們分散式系統只能選擇AP或者CP,但實際上並不是說整個系統只能選擇AP或者CP,在 CAP 理論落地實踐時,我們需要將系統內的資料按照不同的應用場景和要求進行分類,每類資料選擇不同的策略(CP 還是 AP),而不是直接限定整個系統所有資料都是同一策略。

另外,只能選擇CP或者AP是指系統發生分割槽現象時無法同時保證C(一致性)和A(可用性),但不是意味著什麼都不做,當分割槽故障解決後,系統還是要保持保證CA。

CAP理論的延伸——BASE理論

BASE

BASE 是指基本可用(Basically Available)、軟狀態( Soft State)、最終一致性( Eventual Consistency),核心思想是即使無法做到強一致性(CAP 的一致性就是強一致性),但應用可以採用適合的方式達到最終一致性。

  • BA - Basically Available 基本可用 分散式系統在出現故障時,允許損失部分可用性,即保證核心可用。

這裡的關鍵詞是“部分”和“核心”,實際實踐上,哪些是核心需要根據具體業務來權衡。例如登入功能相對註冊功能更加核心,註冊不了最多影響流失一部分使用者,如果使用者已經註冊但無法登入,那就意味使用者無法使用系統,造成的影響範圍更大。

  • S - Soft State 軟狀態 允許系統存在中間狀態,而該中間狀態不會影響系統整體可用性。這裡的中間狀態就是 CAP 理論中的資料不一致。

  • E - Eventual Consistency 最終一致性 系統中的所有資料副本經過一定時間後,最終能夠達到一致的狀態。

這裡的關鍵詞是“一定時間” 和 “最終”,“一定時間”和資料的特性是強關聯的,不同業務不同資料能夠容忍的不一致時間是不同的。例如支付類業務是要求秒級別內達到一致,因為使用者時時關注;使用者發的最新微博,可以容忍30分鐘內達到一致的狀態,因為使用者短時間看不到明星發的微博是無感知的。而“最終”的含義就是不管多長時間,最終還是要達到一致性的狀態。

BASE 理論本質上是對 CAP 的延伸和補充,更具體地說,是對 CAP 中 AP 方案的一個補充:

  • CAP 理論是忽略延時的,而實際應用中延時是無法避免的。 這一點就意味著完美的 CP 場景是不存在的,即使是幾毫秒的資料複製延遲,在這幾毫秒時間間隔內,系統是不符合 CP 要求的。因此 CAP 中的 CP 方案,實際上也是實現了最終一致性,只是“一定時間”是指幾毫秒而已。

  • AP 方案中犧牲一致性只是指發生分割槽故障期間,而不是永遠放棄一致性。 這一點其實就是 BASE 理論延伸的地方,分割槽期間犧牲一致性,但分割槽故障恢復後,系統應該達到最終一致性。

資料一致性模型

前面介紹的BASE模型提過“強一致性”和“最終一致性”,下面對這些一致性模型展開介紹。

分散式系統通過複製資料來提高系統的可靠性和容錯性,並且將資料的不同的副本存放在不同的機器上,由於維護資料副本的一致性代價很高,因此許多系統採用弱一致性來提高效能,下面介紹常見的一致性模型:

  • 強一致性 要求無論更新操作是在哪個資料副本上執行,之後所有的讀操作都要能獲得最新的資料。對於單副本資料來說,讀寫操作是在同一資料上執行的,容易保證強一致性。對多副本資料來說,則需要使用分散式事務協議。

  • 弱一致性 在這種一致性下,使用者讀到某一操作對系統特定資料的更新需要一段時間,我們將這段時間稱為"不一致性視窗"。

  • 最終一致性 是弱一致性的一種特例,在這種一致性下系統保證使用者最終能夠讀取到某操作對系統特定資料的更新(讀取操作之前沒有該資料的其他更新操作)。"不一致性視窗"的大小依賴於互動延遲、系統的負載,以及資料的副本數等。

系統選擇哪種一致性模型取決於應用對一致性的需求,所選取的一致性模型還會影響到系統如何處理使用者的請求以及對副本維護技術的選擇等。後面將基於上面介紹的一致性模型分別介紹分散式事務的解決方案。

柔性事務

柔性事務的概念

在電商等網際網路場景下,傳統的事務在資料庫效能和處理能力上都暴露出了瓶頸。在分散式領域基於CAP理論以及BASE理論,有人就提出了柔性事務的概念。

基於BASE理論的設計思想,柔性事務下,在不影響系統整體可用性的情況下(Basically Available 基本可用),允許系統存在資料不一致的中間狀態(Soft State 軟狀態),在經過資料同步的延時之後,最終資料能夠達到一致。並不是完全放棄了ACID,而是通過放寬一致性要求,藉助本地事務來實現最終分散式事務一致性的同時也保證系統的吞吐

實現柔性事務的一些特性

下面介紹的是實現柔性事務的一些常見特性,這些特性在具體的方案中不一定都要滿足,因為不同的方案要求不一樣。

可見性(對外可查詢) 在分散式事務執行過程中,如果某一個步驟執行出錯,就需要明確的知道其他幾個操作的處理情況,這就需要其他的服務都能夠提供查詢介面,保證可以通過查詢來判斷操作的處理情況。

為了保證操作的可查詢,需要對於每一個服務的每一次呼叫都有一個全域性唯一的標識,可以是業務單據號(如訂單號)、也可以是系統分配的操作流水號(如支付記錄流水號)。除此之外,操作的時間資訊也要有完整的記錄。

操作冪等性 冪等性,其實是一個數學概念。冪等函式,或冪等方法,是指可以使用相同引數重複執行,並能獲得相同結果的函式。冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。也就是說,同一個方法,使用同樣的引數,呼叫多次產生的業務結果與呼叫一次產生的業務結果相同。

之所以需要操作冪等性,是因為為了保證資料的最終一致性,很多事務協議都會有很多重試的操作,如果一個方法不保證冪等,那麼將無法被重試。冪等操作的實現方式有多種,如在系統中快取所有的請求與處理結果、檢測到重複操作後,直接返回上一次的處理結果等。

4 常見分散式事務解決方案

介紹完分散式系統的一致性相關理論,下面基於不同的一致性模型介紹分散式事務的常見解決方案,後面會再介紹各個方案的使用場景。

分散式事務的實現有許多種,其中較經典是由Tuxedo提出的XA分散式事務協議,XA協議包含二階段提交(2PC)和三階段提交(3PC)兩種實現。

4.1 2PC(二階段提交)方案 —— 強一致性

方案簡介

二階段提交協議(Two-phase Commit,即2PC)是常用的分散式事務解決方案,即將事務的提交過程分為兩個階段來進行處理:準備階段和提交階段。事務的發起者稱協調者,事務的執行者稱參與者。

在分散式系統裡,每個節點都可以知曉自己操作的成功或者失敗,卻無法知道其他節點操作的成功或失敗。當一個事務跨多個節點時,為了保持事務的原子性與一致性,而引入一個協調者來統一掌控所有參與者的操作結果,並指示它們是否要把操作結果進行真正的提交或者回滾(rollback)。

二階段提交的演算法思路可以概括為:參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作

核心思想就是對每一個事務都採用先嚐試後提交的處理方式,處理後所有的讀操作都要能獲得最新的資料,因此也可以將二階段提交看作是一個強一致性演算法。

處理流程

簡單一點理解,可以把協調者節點比喻為帶頭大哥,參與者理解比喻為跟班小弟,帶頭大哥統一協調跟班小弟的任務執行。

階段1:準備階段

  • 1、協調者向所有參與者傳送事務內容,詢問是否可以提交事務,並等待所有參與者答覆。
  • 2、各參與者執行事務操作,將undo和redo資訊記入事務日誌中(但不提交事務)。
  • 3、如參與者執行成功,給協調者反饋yes,即可以提交;如執行失敗,給協調者反饋no,即不可提交。

階段2:提交階段 如果協調者收到了參與者的失敗訊息或者超時,直接給每個參與者傳送回滾(rollback)訊息;否則,傳送提交(commit)訊息;參與者根據協調者的指令執行提交或者回滾操作,釋放所有事務處理過程中使用的鎖資源。(注意:必須在最後階段釋放鎖資源) 接下來分兩種情況分別討論提交階段的過程。

情況1,當所有參與者均反饋yes,提交事務

事務正常提交

  • 1、協調者向所有參與者發出正式提交事務的請求(即commit請求)。
  • 2、參與者執行commit請求,並釋放整個事務期間佔用的資源。
  • 3、各參與者向協調者反饋ack(應答)完成的訊息。
  • 4、協調者收到所有參與者反饋的ack訊息後,即完成事務提交。

情況2,當任何階段1一個參與者反饋no,中斷事務

事務中斷

  • 1、協調者向所有參與者發出回滾請求(即rollback請求)。
  • 2、參與者使用階段1中的undo資訊執行回滾操作,並釋放整個事務期間佔用的資源。
  • 3、各參與者向協調者反饋ack完成的訊息。
  • 4、協調者收到所有參與者反饋的ack訊息後,即完成事務中斷。

方案總結

2PC方案實現起來簡單,實際專案中使用比較少,主要因為以下問題:

  • 效能問題 所有參與者在事務提交階段處於同步阻塞狀態,佔用系統資源,容易導致效能瓶頸。
  • 可靠性問題 如果協調者存在單點故障問題,如果協調者出現故障,參與者將一直處於鎖定狀態。
  • 資料一致性問題 在階段2中,如果發生區域性網路問題,一部分事務參與者收到了提交訊息,另一部分事務參與者沒收到提交訊息,那麼就導致了節點之間資料的不一致。

4.2 3PC(三階段提交)方案

方案簡介

三階段提交協議,是二階段提交協議的改進版本,與二階段提交不同的是,引入超時機制。同時在協調者和參與者中都引入超時機制。

三階段提交將二階段的準備階段拆分為2個階段,插入了一個preCommit階段,使得原先在二階段提交中,參與者在準備之後,由於協調者發生崩潰或錯誤,而導致參與者處於無法知曉是否提交或者中止的“不確定狀態”所產生的可能相當長的延時的問題得以解決。

處理流程

階段1:canCommit 協調者向參與者傳送commit請求,參與者如果可以提交就返回yes響應(參與者不執行事務操作),否則返回no響應:

  • 1、協調者向所有參與者發出包含事務內容的canCommit請求,詢問是否可以提交事務,並等待所有參與者答覆。
  • 2、參與者收到canCommit請求後,如果認為可以執行事務操作,則反饋yes並進入預備狀態,否則反饋no。

階段2:preCommit 協調者根據階段1 canCommit參與者的反應情況來決定是否可以基於事務的preCommit操作。根據響應情況,有以下兩種可能。

情況1:階段1所有參與者均反饋yes,參與者預執行事務:

階段2預執行事務

  • 1、協調者向所有參與者發出preCommit請求,進入準備階段。
  • 2、參與者收到preCommit請求後,執行事務操作,將undo和redo資訊記入事務日誌中(但不提交事務)。
  • 3、各參與者向協調者反饋ack響應或no響應,並等待最終指令。

情況2:階段1任何一個參與者反饋no,或者等待超時後協調者尚無法收到所有參與者的反饋,即中斷事務:

階段2中斷事務

  • 1、協調者向所有參與者發出abort請求。
  • 2、無論收到協調者發出的abort請求,或者在等待協調者請求過程中出現超時,參與者均會中斷事務。

階段3:do Commit 該階段進行真正的事務提交,也可以分為以下兩種情況:

情況1:階段2所有參與者均反饋ack響應,執行真正的事務提交:

階段3正式執行事務

  • 1、如果協調者處於工作狀態,則向所有參與者發出do Commit請求。
  • 2、參與者收到do Commit請求後,會正式執行事務提交,並釋放整個事務期間佔用的資源。
  • 3、各參與者向協調者反饋ack完成的訊息。
  • 4、協調者收到所有參與者反饋的ack訊息後,即完成事務提交。

階段2任何一個參與者反饋no,或者等待超時後協調者尚無法收到所有參與者的反饋,即中斷事務:

中斷事務

  • 1、如果協調者處於工作狀態,向所有參與者發出abort請求。
  • 2、參與者使用階段1中的undo資訊執行回滾操作,並釋放整個事務期間佔用的資源。
  • 3、各參與者向協調者反饋ack完成的訊息。
  • 4、協調者收到所有參與者反饋的ack訊息後,即完成事務中斷。

注意:進入階段3後,無論協調者出現問題,或者協調者與參與者網路出現問題,都會導致參與者無法接收到協調者發出的do Commit請求或abort請求。此時,參與者都會在等待超時之後,繼續執行事務提交。

方案總結

  • 優點 相比二階段提交,三階段貼近降低了阻塞範圍,在等待超時後協調者或參與者會中斷事務。避免了協調者單點問題,階段3中協調者出現問題時,參與者會繼續提交事務。

  • 缺點 資料不一致問題依然存在,當在參與者收到preCommit請求後等待do commite指令時,此時如果協調者請求中斷事務,而協調者無法與參與者正常通訊,會導致參與者繼續提交事務,造成資料不一致。

4.3 TCC (Try-Confirm-Cancel)事務 —— 最終一致性

方案簡介

TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland於2007年發表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。

TCC是服務化的二階段程式設計模型,其Try、Confirm、Cancel 3個方法均由業務編碼實現;

  • Try操作作為一階段,負責資源的檢查和預留。
  • Confirm操作作為二階段提交操作,執行真正的業務。
  • Cancel是預留資源的取消。

TCC事務的Try、Confirm、Cancel可以理解為SQL事務中的Lock、Commit、Rollback。

處理流程

為了方便理解,下面以電商下單為例進行方案解析,這裡把整個過程簡單分為扣減庫存,訂單建立2個步驟,庫存服務和訂單服務分別在不同的伺服器節點上。

1、Try 階段 從執行階段來看,與傳統事務機制中業務邏輯相同。但從業務角度來看,卻不一樣。TCC機制中的Try僅是一個初步操作,它和後續的確認一起才能真正構成一個完整的業務邏輯,這個階段主要完成:

  • 完成所有業務檢查( 一致性 )
  • 預留必須業務資源( 準隔離性 )
  • Try 嘗試執行業務 TCC事務機制以初步操作(Try)為中心的,確認操作(Confirm)和取消操作(Cancel)都是圍繞初步操作(Try)而展開。因此,Try階段中的操作,其保障性是最好的,即使失敗,仍然有取消操作(Cancel)可以將其執行結果撤銷。

Try階段

假設商品庫存為100,購買數量為2,這裡檢查和更新庫存的同時,凍結使用者購買數量的庫存,同時建立訂單,訂單狀態為待確認。

2、Confirm / Cancel 階段 根據Try階段服務是否全部正常執行,繼續執行確認操作(Confirm)或取消操作(Cancel)。 Confirm和Cancel操作滿足冪等性,如果Confirm或Cancel操作執行失敗,將會不斷重試直到執行完成。

Confirm:當Try階段服務全部正常執行, 執行確認業務邏輯操作

Confirm

這裡使用的資源一定是Try階段預留的業務資源。在TCC事務機制中認為,如果在Try階段能正常的預留資源,那Confirm一定能完整正確的提交。Confirm階段也可以看成是對Try階段的一個補充,Try+Confirm一起組成了一個完整的業務邏輯。

Cancel:當Try階段存在服務執行失敗, 進入Cancel階段

Cancel
Cancel取消執行,釋放Try階段預留的業務資源,上面的例子中,Cancel操作會把凍結的庫存釋放,並更新訂單狀態為取消。

方案總結

TCC事務機制相對於傳統事務機制(X/Open XA),TCC事務機制相比於上面介紹的XA事務機制,有以下優點:

  • 效能提升 具體業務來實現控制資源鎖的粒度變小,不會鎖定整個資源。
  • 資料最終一致性 基於Confirm和Cancel的冪等性,保證事務最終完成確認或者取消,保證資料的一致性。
  • 可靠性 解決了XA協議的協調者單點故障問題,由主業務方發起並控制整個業務活動,業務活動管理器也變成多點,引入叢集。

缺點: TCC的Try、Confirm和Cancel操作功能要按具體業務來實現,業務耦合度較高,提高了開發成本。

4.4 本地訊息表 —— 最終一致性

方案簡介

本地訊息表的方案最初是由ebay提出,核心思路是將分散式事務拆分成本地事務進行處理。

方案通過在事務主動發起方額外新建事務訊息表,事務發起方處理業務和記錄事務訊息在本地事務中完成,輪詢事務訊息表的資料傳送事務訊息,事務被動方基於訊息中介軟體消費事務訊息表中的事務。

這樣設計可以避免”業務處理成功 + 事務訊息傳送失敗",或"業務處理失敗 + 事務訊息傳送成功"的棘手情況出現,保證2個系統事務的資料一致性。

處理流程

下面把分散式事務最先開始處理的事務方成為事務主動方,在事務主動方之後處理的業務內的其他事務成為事務被動方。

為了方便理解,下面繼續以電商下單為例進行方案解析,這裡把整個過程簡單分為扣減庫存,訂單建立2個步驟,庫存服務和訂單服務分別在不同的伺服器節點上,其中庫存服務是事務主動方,訂單服務是事務被動方。

事務的主動方需要額外新建事務訊息表,用於記錄分散式事務的訊息的發生、處理狀態。

整個業務處理流程如下:

本地訊息表方案

  • 步驟1 事務主動方處理本地事務。 事務主動發在本地事務中處理業務更新操作和寫訊息表操作。 上面例子中庫存服務階段再本地事務中完成扣減庫存和寫訊息表(圖中1、2)。
  • 步驟2 事務主動方通過訊息中介軟體,通知事務被動方處理事務通知事務待訊息。 訊息中介軟體可以基於Kafka、RocketMQ訊息佇列,事務主動方法主動寫訊息到訊息佇列,事務消費方消費並處理訊息佇列中的訊息。 上面例子中,庫存服務把事務待處理訊息寫到訊息中介軟體,訂單服務消費訊息中介軟體的訊息,完成新增訂單(圖中3 - 5)。
  • 步驟3 事務被動方通過訊息中介軟體,通知事務主動方事務已處理的訊息。 上面例子中,訂單服務把事務已處理訊息寫到訊息中介軟體,庫存服務消費中介軟體的訊息,並將事務訊息的狀態更新為已完成(圖中6 - 8)

為了資料的一致性,當處理錯誤需要重試,事務傳送方和事務接收方相關業務處理需要支援冪等。具體儲存一致性的容錯處理如下:

  • 1、當步驟1處理出錯,事務回滾,相當於什麼都沒發生。
  • 2、當步驟2、步驟3處理出錯,由於未處理的事務訊息還是儲存在事務傳送方,事務傳送方可以定時輪詢為超時訊息資料,再次傳送的訊息中介軟體進行處理。事務被動方消費事務訊息重試處理。
  • 3、如果是業務上的失敗,事務被動方可以發訊息給事務主動方進行回滾。
  • 4、如果多個事務被動方已經消費訊息,事務主動方需要回滾事務時需要通知事務被動方回滾。

方案總結

方案的優點如下:

  • 從應用設計開發的角度實現了訊息資料的可靠性,訊息資料的可靠性不依賴於訊息中介軟體,弱化了對MQ中介軟體特性的依賴。
  • 方案輕量,容易實現。

缺點如下:

  • 與具體的業務場景繫結,耦合性強,不可公用。
  • 訊息資料與業務資料同庫,佔用業務系統資源。
  • 業務系統在使用關係型資料庫的情況下,訊息服務效能會受到關係型資料庫併發效能的侷限。

4.5 MQ事務 —— 最終一致性

方案簡介

基於MQ的分散式事務方案其實是對本地訊息表的封裝,將本地訊息表基於MQ 內部,其他方面的協議基本與本地訊息表一致。

處理流程

下面主要基於RocketMQ4.3之後的版本介紹MQ的分散式事務方案。

在本地訊息表方案中,保證事務主動方發寫業務表資料和寫訊息表資料的一致性是基於資料庫事務,RocketMQ的事務訊息相對於普通MQ,相對於提供了2PC的提交介面,方案如下:

正常情況——事務主動方發訊息 這種情況下,事務主動方服務正常,沒有發生故障,發訊息流程如下:

正常情況——事務主動方發訊息

  • 圖中1、傳送方向 MQ服務端(MQ Server)傳送half訊息。
  • 圖中2、MQ Server 將訊息持久化成功之後,向傳送方 ACK 確認訊息已經傳送成功。
  • 圖中3、傳送方開始執行本地事務邏輯。
  • 圖中4、傳送方根據本地事務執行結果向 MQ Server 提交二次確認(commit 或是 rollback)。
  • 圖中5、MQ Server 收到 commit 狀態則將半訊息標記為可投遞,訂閱方最終將收到該訊息;MQ Server 收到 rollback 狀態則刪除半訊息,訂閱方將不會接受該訊息。

異常情況——事務主動方訊息恢復 在斷網或者應用重啟等異常情況下,圖中4提交的二次確認超時未到達 MQ Server,此時處理邏輯如下:

異常情況——事務主動方訊息恢復

  • 圖中5、MQ Server 對該訊息發起訊息回查。
  • 圖中6、傳送方收到訊息回查後,需要檢查對應訊息的本地事務執行的最終結果。
  • 圖中7、傳送方根據檢查得到的本地事務的最終狀態再次提交二次確認
  • 圖中8、MQ Server基於commit / rollback 對訊息進行投遞或者刪除

介紹完RocketMQ的事務訊息方案後,由於前面已經介紹過本地訊息表方案,這裡就簡單介紹RocketMQ分散式事務:

MQ分散式事務

事務主動方基於MQ通訊通知事務被動方處理事務,事務被動方基於MQ返回處理結果。 如果事務被動方消費訊息異常,需要不斷重試,業務處理邏輯需要保證冪等。 如果是事務被動方業務上的處理失敗,可以通過MQ通知事務主動方進行補償或者事務回滾。

方案總結

相比本地訊息表方案,MQ事務方案優點是:

  • 訊息資料獨立儲存 ,降低業務系統與訊息系統之間的耦合。
  • 吞吐量由於使用本地訊息表方案。

缺點是:

  • 一次訊息傳送需要兩次網路請求(half訊息 + commit/rollback訊息)
  • 業務處理服務需要實現訊息狀態回查介面

4.6 Saga事務 —— 最終一致性

方案簡介

Saga事務源於1987年普林斯頓大學的Hecto和Kenneth發表的如何處理long lived transaction(長活事務)論文,Saga事務核心思想是將長事務拆分為多個本地短事務,由Saga事務協調器協調,如果正常結束那就正常完成,如果某個步驟失敗,則根據相反順序一次呼叫補償操作。

處理流程

Saga事務基本協議如下

  • 每個Saga事務由一系列冪等的有序子事務(sub-transaction) Ti 組成。
  • 每個Ti 都有對應的冪等補償動作Ci,補償動作用於撤銷Ti造成的結果。

可以看到,和TCC相比,Saga沒有“預留”動作,它的Ti就是直接提交到庫。

下面以下單流程為例,整個操作包括:建立訂單、扣減庫存、支付、增加積分 Saga的執行順序有兩種:

Saga事務執行順序

  • 事務正常執行完成 T1, T2, T3, ..., Tn,例如:扣減庫存(T1),建立訂單(T2),支付(T3),依次有序完成整個事務。
  • 事務回滾 T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n,例如:扣減庫存(T1),建立訂單(T2),支付(T3,支付失敗),支付回滾(C3),訂單回滾(C2),恢復庫存(C1)。

Saga定義了兩種恢復策略:

  • 向前恢復(forward recovery)

Saga事務向前恢復

對應於上面第一種執行順序,適用於必須要成功的場景,發生失敗進行重試,執行順序是類似於這樣的:T1, T2, ..., Tj(失敗), Tj(重試),..., Tn,其中j是發生錯誤的子事務(sub-transaction)。該情況下不需要Ci。

  • 向後恢復(backward recovery)

Saga事務向後恢復

對應於上面提到的第二種執行順序,其中j是發生錯誤的子事務(sub-transaction),這種做法的效果是撤銷掉之前所有成功的子事務,使得整個Saga的執行結果撤銷。

Saga事務常見的有兩種不同的實現方式:

  • 1、命令協調(Order Orchestrator):中央協調器負責集中處理事件的決策和業務邏輯排序。

中央協調器(Orchestrator,簡稱OSO)以命令/回覆的方式與每項服務進行通訊,全權負責告訴每個參與者該做什麼以及什麼時候該做什麼。

命令協調模式

以電商訂單的例子為例:

1、事務發起方的主業務邏輯請求OSO服務開啟訂單事務 2、OSO向庫存服務請求扣減庫存,庫存服務回覆處理結果。 3、OSO向訂單服務請求建立訂單,訂單服務回覆建立結果。 4、OSO向支付服務請求支付,支付服務回覆處理結果。 5、主業務邏輯接收並處理OSO事務處理結果回覆。

中央協調器必須事先知道執行整個訂單事務所需的流程(例如通過讀取配置)。如果有任何失敗,它還負責通過向每個參與者傳送命令來撤銷之前的操作來協調分散式的回滾。基於中央協調器協調一切時,回滾要容易得多,因為協調器預設是執行正向流程,回滾時只要執行反向流程即可。

  • 2、事件編排 (Event Choreography0:沒有中央協調器(沒有單點風險)時,每個服務產生並觀察其他服務的事件,並決定是否應採取行動

在事件編排方法中,第一個服務執行一個事務,然後釋出一個事件。該事件被一個或多個服務進行監聽,這些服務再執行本地事務併發布(或不釋出)新的事件。

當最後一個服務執行本地事務並且不釋出任何事件時,意味著分散式事務結束,或者它釋出的事件沒有被任何Saga參與者聽到都意味著事務結束。

以電商訂單的例子為例:

事件編排模式

1、事務發起方的主業務邏輯釋出開始訂單事件 2、庫存服務監聽開始訂單事件,扣減庫存,併發布庫存已扣減事件 2、訂單服務監聽庫存已扣減事件,建立訂單,併發布訂單已建立事件 4、支付服務監聽訂單已建立事件,進行支付,併發布訂單已支付事件 5、主業務邏輯監聽訂單已支付事件並處理。

事件/編排是實現Saga模式的自然方式,它很簡單,容易理解,不需要太多的程式碼來構建。如果事務涉及2至4個步驟,則可能是非常合適的。

方案總結

命令協調設計的優點和缺點: 優點如下:

  • 1、服務之間關係簡單,避免服務之間的迴圈依賴關係,因為Saga協調器會呼叫Saga參與者,但參與者不會呼叫協調器
  • 2、程式開發簡單,只需要執行命令/回覆(其實回覆訊息也是一種事件訊息),降低參與者的複雜性。
  • 3、易維護擴充套件,在新增新步驟時,事務複雜性保持線性,回滾更容易管理,更容易實施和測試

缺點如下:

  • 1、中央協調器容易處理邏輯容易過於複雜,導致難以維護。
  • 2、存在協調器單點故障風險。

事件/編排設計的優點和缺點 優點如下:

  • 1、避免中央協調器單點故障風險。
  • 2、當涉及的步驟較少服務開發簡單,容易實現。

缺點如下:

  • 1、服務之間存在迴圈依賴的風險。
  • 2、當涉及的步驟較多,服務間關係混亂,難以追蹤調測。

值得補充的是,由於Saga模型中沒有Prepare階段,因此事務間不能保證隔離性,當多個Saga事務操作同一資源時,就會產生更新丟失、髒資料讀取等問題,這時需要在業務層控制併發,例如:在應用層面加鎖,或者應用層面預先凍結資源。

5 總結

各方案使用場景

介紹完分散式事務相關理論和常見解決方案後,最終的目的在實際專案中運用,因此,總結一下各個方案的常見的使用場景。

方案比較

  • 2PC/3PC 依賴於資料庫,能夠很好的提供強一致性和強事務性,但相對來說延遲比較高,比較適合傳統的單體應用,在同一個方法中存在跨庫操作的情況,不適合高併發和高效能要求的場景。

  • TCC 適用於執行時間確定且較短,實時性要求高,對資料一致性要求高,比如網際網路金融企業最核心的三個服務:交易、支付、賬務。

  • 本地訊息表/MQ事務 都適用於事務中參與方支援操作冪等,對一致性要求不高,業務上能容忍資料不一致到一個人工檢查週期,事務涉及的參與方、參與環節較少,業務上有對賬/校驗系統兜底。

  • Saga事務 由於Saga事務不能保證隔離性,需要在業務層控制併發,適合於業務場景事務併發操作同一資源較少的情況。 Saga相比缺少預提交動作,導致補償動作的實現比較麻煩,例如業務是傳送簡訊,補償動作則得再傳送一次簡訊說明撤銷,使用者體驗比較差。Saga事務較適用於補償動作容易處理的場景。

分散式事務方案設計

本文介紹的偏向於原理,業界已經有不少開源的或者收費的解決方案,篇幅所限,就不再展開介紹。

實際運用理論時進行架構設計時,許多人容易犯“手裡有了錘子,看什麼都覺得像釘子”的錯誤,設計方案時考慮的問題場景過多,各種重試,各種補償機制引入系統,導致設計出來的系統過於複雜,落地遙遙無期。

世界上解決一個計算機問題最簡單的方法:“恰好”不需要解決它!—— 阿里中介軟體技術專家沈詢

有些問題,看起來很重要,但實際上我們可以通過合理的設計或者將問題分解來規避。設計分散式事務系統也不是需要考慮所有異常情況,不必過度設計各種回滾,補償機制。如果硬要把時間花在解決問題本身,實際上不僅效率低下,而且也是一種浪費。

如果系統要實現回滾流程的話,有可能系統複雜度將大大提升,且很容易出現Bug,估計出現Bug的概率會比需要事務回滾的概率大很多。在設計系統時,我們需要衡量是否值得花這麼大的代價來解決這樣一個出現概率非常小的問題,可以考慮當出現這個概率很小的問題,能否採用人工解決的方式,這也是大家在解決疑難問題時需要多多思考的地方。

(本文同時發表於作者個人部落格 www.jianshu.com/p/e62e01a48…)

參考

technology-talk —— 事務

MySQL 中事務的實現

分散式一致性演算法2PC和3PC

分散式開放訊息系統(RocketMQ)的原理與實踐

RocketMQ事務訊息入門介紹

Saga分散式事務解決方案與實踐 —— 姜寧

分散式事務Saga模式

從一筆金幣充值去思考分散式事務

相關文章