讓咱們聊一聊分佈式事務

一個複雜的系統每每都是從一個小而簡的系統發展衍化而來,爲了知足日益增加的業務需求,不斷的增長系統的複雜度,從單體架構逐步發展爲分佈式架構,而分佈式系統架構的設計主要關注:高性能,高可用,高拓展java

分佈式事務

高可用是指系統無中斷的執行功能的能了,表明了系統的可用程度,是進行系統設計時必需要遵照的準則之一。mysql

而高可用的實現方案,無外乎就是冗餘,就存儲的高可用而言,問題不在於如何進行數據備份,而在於如何規避數據不一致對業務形成的影響git

對於分佈式系統而言,要保證分佈式系統中的數據一致性就須要一種方案,能夠保證數據在子系統中始終保持一致,避免業務出現問題,這種實現方案就叫作分佈式事務,要麼一塊兒成功,要麼一塊兒失敗,必須是一個總體性的事務github

理論基礎

​ 在講解具體方案以前,有必要了解一下分佈式中數據設計須要遵循的理論基礎,CAP理論和BACS理論,爲後面的實踐鋪平道路算法

CAP理論

CAP:Consistency Acailability Partition tolerance 的簡寫sql

  • Consistency:一致性數據庫

    對某個客戶端來講,讀操做可以返回最新的寫操做結果編程

  • Acailability:可用性設計模式

    非故障節點在合理的時間內返回合理的響應網絡

  • Partition tolerance:分區容錯性

    當出現網絡分區後,系統可以繼續提供服務 你知道什麼是網絡分區嗎 ~~

由於分佈式系統中系統確定部署在多臺機器上,沒法保證網絡作到100%的可靠,因此網絡分區必定存在,即P必定存在;

在出現網絡分區後,就出現了可用性和一致性的問題,咱們必需要在這二者之間進行取捨,所以就有了兩種架構:CP架構,AP架構;

CP架構

當網絡分區出現後,爲了保證一致性,就必須拒接請求,不然沒法保證一致性

  1. 當沒有出網絡分區時,系統A與系統B的數據一致,X=1
  2. 將系統A的X修改成2,X=2
  3. 當出現網絡分區後,系統A與系統B之間的數據同步數據失敗,系統B的X=1
  4. 當客戶端請求系統B時,爲了保證一致性,此時系統B應拒絕服務請求,返回錯誤碼或錯誤信息

上面這種方式就違背了可用性的要求,只知足一致性和分區容錯,即CP

CAP理論是忽略網絡延遲,從系統A同步數據到系統B的網絡延遲是忽略的

CP架構保證了客戶端在獲取數據時必定是最近的寫操做,或者獲取到異常信息,毫不會出現數據不一致的狀況

AP架構

當網絡分區出現後,爲了保證可用性,系統B能夠返回舊值,保證系統的可用性

  1. 當沒有出網絡分區時,系統A與系統B的數據一致,X=1
  2. 將系統A的X修改成2,X=2
  3. 當出現網絡分區後,系統A與系統B之間的數據同步數據失敗,系統B的X=1
  4. 當客戶端請求系統B時,爲了保證可用性,此時系統B應返回舊值,X=1

上面這種方式就違背了一致性的要求,只知足可用性和分區容錯,即AP

AP架構保證了客戶端在獲取數據時不管返回的是最新值仍是舊值,系統必定是可用的

CAP理論關注粒度是數據,而不是總體系統設計的策略

BASE理論

​ BASE理論指的是基本可用 Basically Available,軟狀態 Soft Stat,最終一致性 Eventual Consistency,核心思想是即使沒法作到強一致性,但應該能夠有采用適合的方式保證最終一致性

BASE:Basically Available Soft Stat Eventual Consistency的簡寫

  • BA:Basically Available 基本可用

    分佈式系統在出現故障的時候,容許損失部分可用性,即保證核心可用

  • S:Soft Stat 軟狀態

    容許系統存在中間狀態,而該中間狀態不會影響系統總體可用性

  • E:Consistency 最終一致性

    系統中的全部數據副本通過必定時間後,最終可以達到一致的狀態

BASE理論本質上是對CAP理論的延伸,是對 CAP 中 AP 方案的一個補充

分佈式事務協議

X/Open XA 協議

XA是一個分佈式事務協議,由Tuxedo提出。XA規範主要定義了(全局)事務管理器(Transaction Manager)和(局部)資源管理器(Resource Manager)之間的接口。XA接口是雙向的系統接口,在事務管理器Transaction Manager)以及一個或多個資源管理器(Resource Manager)之間造成通訊橋樑

XA協議採用兩階段提交方式來管理分佈式事務。XA接口提供資源管理器與事務管理器之間進行通訊的標準接口

2PC 二階段提交 協議

​ 二階段提交(Two-phase Commit),是指,爲了使基於分佈式系統架構下的全部節點在進行事務提交時保持一致性而設計的一種算法(Algorithm)。一般,二階段提交也被稱爲是一種協議(Protocol)。在分佈式系統中,每一個節點雖然能夠知曉本身的操做時成功或者失敗,卻沒法知道其餘節點的操做的成功或失敗。當一個事務跨越多個節點時,爲了保持事務的ACID特性,須要引入一個做爲協調者的組件來統一掌控全部節點(稱做參與者)的操做結果並最終指示這些節點是否要把操做結果進行真正的提交(好比將更新後的數據寫入磁盤等等)。所以,二階段提交的算法思路能夠歸納爲: 參與者將操做成敗通知協調者,再由協調者根據全部參與者的反饋情報決定各參與者是否要提交操做仍是停止操做

二階段提交算法的成立基於如下假設:

  1. 該分佈式系統中,存在一個節點做爲協調者(Coordinator),其餘節點做爲參與者(Cohorts)。且節點之間能夠進行網絡通訊。
  2. 全部節點都採用預寫式日誌,且日誌被寫入後即被保持在可靠的存儲設備上,即便節點損壞不會致使日誌數據的消失。
  3. 全部節點不會永久性損壞,即便損壞後仍然能夠恢復

二階段提交分爲兩階段:第一階段:投票階段,第二階段:提交階段

投票階段 Prepares

  1. 協調者向全部參與者詢問是否能夠執行提交操做,並開始等待各參與者的響應
  2. 參與者執行事務操做,若是執行成功就返回Yes響應,若是執行失敗就返回No響應
  3. 若是協調者接受參與者響應超時,也會認爲執行事務操做失敗

提交階段 commit

  1. 若是第一階段匯中全部參與者都返回yes響應,協調者向全部參與者發出提交請求,全部參與者提交事務
  2. 若是第一階段中有一個或者多個參與者返回no響應,協調者向全部參與者發出回滾請求,全部參與者進行回滾操做

二階段提交優勢:儘可能保證了數據的強一致,但不是100%一致

缺點:

  • 單點故障

    因爲協調者的重要性,一旦協調者發生故障,參與者會一直阻塞,尤爲時在第二階段,協調者發生故障,那麼全部的參與者都處於鎖定事務資源的狀態中,而沒法繼續完成事務操做

  • 同步阻塞

    因爲全部節點在執行操做時都是同步阻塞的,當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態

  • 數據不一致

    在第二階段中,當協調者想參與者發送提交事務請求以後,發生了局部網絡異常或者在發送提交事務請求過程當中協調者發生了故障,這會致使只有一部分參與者接收到了提交事務請求。而在這部分參與者接到提交事務請求以後就會執行提交事務操做。可是其餘部分未接收到提交事務請求的參與者則沒法提交事務。從而致使分佈式系統中的數據不一致

二階段提交的問題

​ 若是協調者在第二階段發送提交請求以後掛掉,而惟一接受到這條消息的參與者執行以後也掛掉了,即便協調者經過選舉協議產生了新的協調者並通知其餘參與者進行提交或回滾操做的話,均可能會與這個已經執行的參與者執行的操做不同,當這個掛掉的參與者恢復以後,就會產生數據不一致的問題

3PC 三階段提交 協議

​ 三階段提交(Three-phase commit),三階段提交是爲解決兩階段提交協議|的缺點而設計的。 與兩階段提交不一樣的是,三階段提交是「非阻塞」協議。三階段提交在兩階段提交的第一階段與第二階段之間插入了一個準備階段,使得原先在兩階段提交中,參與者在投票以後,因爲協調者發生崩潰或錯誤,而致使參與者處於沒法知曉是否提交或者停止的「不肯定狀態」所產生的可能至關長的延時的問題得以解決

三階段提交的三個階段:CanCommit,PreCommit,DoCommit三個階段

詢問階段 CanCommit

協調者向參與者發送commit請求,參與者若是能夠提交就返回Yes響應,不然返回No響應

準備階段 PreCommit

協調者根據參與者在詢問階段的響應判斷是否執行事務仍是中斷事務

  • 若是全部參與者都返回Yes,則執行事務
  • 若是參與者有一個或多個參與者返回No或者超時,則中斷事務

參與者執行完操做以後返回ACK響應,同時開始等待最終指令

提交階段 DoCommit

協調者根據參與者在準備階段的響應判斷是否執行事務仍是中斷事務

  • 若是全部參與者都返回正確的ACK響應,則提交事務
  • 若是參與者有一個或多個參與者收到錯誤的ACK響應或者超時,則中斷事務
  • 若是參與者沒法及時接收到來自協調者的提交或者中斷事務請求時,會在等待超時以後,會繼續進行事務提交

協調者收到全部參與者的ACK響應,完成事務

解決二階段提交時的問題

​ 在三階段提交中,若是在第三階段協調者發送提交請求以後掛掉,而且惟一的接受的參與者執行提交操做以後也掛掉了,這時協調者經過選舉協議產生了新的協調者,在二階段提交時存在的問題就是新的協調者不肯定已經執行過事務的參與者是執行的提交事務仍是中斷事務,可是在三階段提交時,確定獲得了第二階段的再次確認,那麼第二階段必然是已經正確的執行了事務操做,只等待提交事務了,因此新的協調者能夠從第二階段中分析出應該執行的操做,進行提交或者中斷事務操做,這樣即便掛掉的參與者恢復過來,數據也是一致的。

​ 因此,三階段提交解決了二階段提交中存在的因爲協調者和參與者同時掛掉可能致使的數據一致性問題和單點故障問題,並減小阻塞,由於一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行提交事務,而不會一直持有事務資源並處於阻塞狀態。

三階段提交的問題

​ 在提交階段若是發送的是中斷事務請求,可是因爲網絡問題,致使部分參與者沒有接到請求,那麼參與者會在等待超時以後執行提交事務操做,這樣這些因爲網絡問題致使提交事務的參與者的數據就與接受到中斷事務請求的參與者存在數據不一致的問題。

因此不管是2PC仍是3PC都不能保證分佈式系統中的數據100%一致

解決方案

舉個栗子:

​ 在電商網站中,用戶對商品進行下單,須要在訂單表中建立一條訂單數據,同時須要在庫存表中修改當前商品的剩餘庫存數量,兩步操做一個添加,一個修改,咱們必定要保證這兩步操做必定同時操做成功或失敗,不然業務就會出現問題

創建時:

​ 業務量不大,用戶少,系統只是一個單體架構,訂單表與庫存表都在一個數據庫中,這時可使用mysql的本地事務保證數據一致性

發展期:

​ 業務發展迅速,用戶量變多,單數據已經出現了性能瓶頸,按照業務緯度進行分庫,分爲訂單庫和庫存庫,因爲跨庫跨機器,mysql的本地事務不能再保證訂單庫和庫存庫的數據一致性

成熟期:

​ 業務拓展,單體架構已經知足不了需求,進而衍化成了分佈式系統,這時的訂單和庫存已經拆分爲了兩個子系統提供服務,子系統間使用rpc進行通訊,可是不管系統發展成什麼樣,咱們都要保證業務不出問題,保證訂單和庫存的數據一致,這時候要思考下在服務之間咱們應如何保證數據一致

強一致性分佈式事務

單體架構多數據源,在業務開發中,確定是先執行對訂單庫的操做,可是不提交事務,再執行對庫存庫的操做,也不提交事務,若是兩個操做都成功,在一塊兒提交事務,若是有一個操做失敗,則兩個都進行回滾

基於2PC/XA協議實現的JTA

咱們已經知道了2PC和XA協議的原理,而JTA是java規範,是XA在java上的實現

JTA(Java Transaction Manager) :

  1. TransactionManager : 經常使用方法,能夠開啓,回滾,獲取事務. begin(),rollback()...
  2. XAResouce : 資源管理,經過Session來進行事務管理,commit(xid)...
  3. XID : 每個事務都分配一個特定的XID

JTA主要的原理是二階段提交,當整個業務完成了以後只是第一階段提交,在第二階段提交以前會檢查其餘全部事務是否已經提交,若是前面出現了錯誤或是沒有提交,那麼第二階段就不會提交,而是直接回滾,這樣全部的事務都會作回滾操做

基於JTA這種方案實現分佈式事務的強一致性

JTA的特色:

  • 基於兩階段提交,有可能會出現數據不一致的狀況
  • 事務時間過長,阻塞
  • 性能低,吞吐量低

實現可使用基於JTA實現的jar包Atomikos 使用例子能夠本身百度一下

​ 正常架構設計中是否應該出現這種跨庫的操做,我以爲是不該該的,若是過按業務拆分將數據源進行分庫,咱們應該同時將服務也拆分出去才合適,應遵循一個系統只操做一個數據源(主從不要緊),避免後續可能會出現的多個系統調用一個數據源的狀況

最終一致性分佈式事務方案(柔性事務)

JTA方案適用於單體架構多數據源時實現分佈式事務,但對於微服務間的分佈式事務就無能爲力了,咱們須要使用其餘的方案實現分佈式事務

本地消息表

本地消息表的核心思想是將分佈式事務拆分紅本地事務進行處理

以本文中例子,在訂單系統新增一條消息表,將新增訂單和新增消息放到一個事務裏完成,而後經過輪詢的方式去查詢消息表,將消息推送到mq,庫存系統去消費mq

執行流程:

  1. 訂單系統,添加一條訂單和一條消息,在一個事務裏提交
  2. 訂單系統,使用定時任務輪詢查詢狀態爲未同步的消息表,發送到mq,若是發送失敗,就重試發送
  3. 庫存系統,接收mq消息,修改庫存表,須要保證冪等操做
  4. 若是修改爲功,調用rpc接口修改訂單系統消息表的狀態爲已完成或者直接刪除這條消息
  5. 若是修改失敗,能夠不作處理,等待重試

訂單系統中的消息有可能因爲業務問題會一直重複發送,因此爲了不這種狀況能夠記錄一下 發送次數,當達到次數限制以後報警,人工接入處理;庫存系統須要保證冪等,避免同一條消息被屢次消費形成數據一致;

本地消息表這種方案實現了最終一致性,須要在業務系統裏增長消息表,業務邏輯中多一次插入的DB操做,因此性能會有損耗,並且最終一致性的間隔主要有定時任務的間隔時間決定

MQ消息事務

消息事務的原理是將兩個事務經過消息中間件進行異步解耦

訂單系統執行本身的本地事務,併發送mq消息,庫存系統接收消息,執行本身的本地事務,乍一看,好像跟本地消息表的實現方案相似,只是省去 了對本地消息表的操做和輪詢發送mq的操做,但實際上兩種方案的實現是不同的

消息事務必定要保證業務操做與消息發送的一致性,若是業務操做成功,這條消息也必定投遞成功

消息事務依賴於消息中間件的事務消息,基於消息中間件的二階段提交實現的,RocketMQ就支持事務消息

執行流程:

  1. 發送prepare消息到消息中間件
  2. 發送成功後,執行本地事務
  3. 若是事務執行成功,則commit,消息中間件將消息下發至消費端
  4. 若是事務執行失敗,則回滾,消息中間件將這條prepare消息刪除
  5. 消費端接收到消息進行消費,若是消費失敗,則不斷重試

這種方案也是實現了最終一致性,對比本地消息表實現方案,不須要再建消息表,再也不依賴本地數據庫事務了,因此這種方案更適用於高併發的場景

最大努力通知

最大努力通知相比前兩種方案實現簡單,適用於一些最終一致性要求較低的業務,好比支付通知,短信通知這種業務

以支付通知爲例,業務系統調用支付平臺進行支付,支付平臺進行支付,進行操做支付以後支付平臺會盡可能去通知業務系統支付操做是否成功,可是會有一個最大通知次數,若是超過這個次數後仍是通知失敗,就再也不通知,業務系統自行調用支付平臺提供一個查詢接口,供業務系統進行查詢支付操做是否成功

執行流程:

  1. 業務系統調用支付平臺支付接口, 並在本地進行記錄,支付狀態爲支付中
  2. 支付平臺進行支付操做以後,不管成功仍是失敗,都須要給業務系統一個結果通知
  3. 若是通知一直失敗則根據重試規則進行重試,達到最大通知次數後,不在通知
  4. 支付平臺提供查詢訂單支付操做結果接口
  5. 業務系統根據必定業務規則去支付平臺查詢支付結果

這種方案也是實現了最終一致性

補償事務TCC

TCC Try-Confirm-Cancel的簡稱,針對每一個操做,都須要有一個其對應的確認和取消操做,當操做成功時調用確認操做,當操做失敗時調用取消操做,相似於二階段提交,只不過是這裏的提交和回滾是針對業務上的,因此基於TCC實現的分佈式事務也能夠看作是對業務的一種補償機制

TCC的三階段:

  1. Try 階段:對業務系統作檢測及資源預留
  2. Confirm 階段:對業務系統作確認提交,Try階段執行成功並開始執行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm必定成功
  3. Cancel 階段:在業務執行錯誤,須要回滾的狀態下執行的業務取消,預留資源釋放

在try階段,是對業務系統進行檢查及資源預覽,好比訂單和存儲操做,須要檢查庫存剩餘數量是否夠用,並進行預留,預留操做的話就是新建一個可用庫存數量字段,Try階段操做是對這個可用庫存數量進行操做

好比下一個訂單減一個庫存:

執行流程:

  1. Try階段:訂單系統將當前訂單狀態設置爲支付中,庫存系統校驗當前剩餘庫存數量是否大於1,而後將可用庫存數量設置爲庫存剩餘數量-1,
  2. 若是Try階段執行成功,執行Confirm 階段,將訂單狀態修改成支付成功,庫存剩餘數量修改成可用庫存數量
  3. 若是Try階段執行失敗,執行Cancel 階段,將訂單狀態修改成支付失敗,可用庫存數量修改成庫存剩餘數量

基於TCC實現分佈式事務,代碼邏輯想對複雜一些,須要將原來的接口的邏輯拆分爲:try,confirm ,cancel 三個接口的邏輯

基於TCC實現的分佈式事務框架:ByteTCC,tcc-transaction

ByteTCC:github.com/liuyangming…

tcc-transaction:github.com/changmingxi…

讀完以後應該對分佈式事務有了一個大體的瞭解,在實際生產中咱們要儘可能避免使用分佈式事務,能轉化爲本地事務就用本地事務,若是必須使用分佈式事務,還須要從業務角度多思考使用哪一種方案更適合

總之行動以前多思考

推薦閱讀:

23種設計模式詳解

淺析網絡協議

java併發編程 | 鎖詳解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

java併發編程 | 線程池詳解

參考:

百度百科

分佈式事務解決方案

深刻理解分佈式事務

深刻理解分佈式系統的2PC和3PC

柔性事務:最大努力通知

相關文章
相關標籤/搜索