編寫本身的分佈式框架—事務解決方案

前言

工欲善其事必先利其器,既然咱們決定要作一個分佈式事務框架,那首先須要瞭解一下,分佈式事務是怎麼回事,它跟傳統的本地事務有什麼區別,解決方案有哪些,每種解決方案的對比等等。mysql

本地事務

在瞭解分佈式事務以前,先回顧一下本地事務,顧名思義,本地事務就是在同一個JVM中,一個開啓了事務的業務方法就是本地事務。而這一個開啓了事務的業務方法裏面的操做要麼所有執行成功,要麼所有執行失敗,不容許只成功一半另一半執行失敗的事情發生。例如該業務方法中,有兩次數據庫更新操做,那麼這兩次數據庫操做要麼所有執行成功,要麼所有回滾。使用專業術語來說的話,就是事務的4個基本特性:Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durablity(持久性),統稱ACID,這裏簡單的對ACID作一個概念的說明,看成是作個筆記:sql

  • Atomicity(原子性)
    是指事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。通俗的說,就是全部操做要麼所有成功,要麼所有失敗回滾。
  • Consistency(一致性)
    是指事務執行先後,數據從一個狀態到另外一個狀態必須是一致的,好比A向B轉帳(A、B的總金額就是一個一致性狀態),不可能出現A扣了錢,B卻沒收到的狀況發生。
  • Isolation(隔離性)
    是指當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。這裏涉及到數據庫的隔離級別的概念,不是咱們討論的主題,不詳細展開,你們能夠自行查閱相關資料。
  • Durablity(持久性)
    是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。

分佈式事務

經過以上的回顧咱們知道,本地事務對於咱們來講不是什麼問題,由於咱們能夠直接使用數據庫的事務支持,好比mysql、oracle這些數據庫對事務都有很好的支持。可是,對於分佈式應用來講的話,事務就沒有那麼簡單了,由於須要開啓事務的業務方法,極可能是分佈在不一樣應用程序中,這就說明,你們不在同一個JVM中,事務空間都不同了,那就沒辦法作到要麼所有執行成功,要麼所有執行失敗了,咱們能夠看如下分析圖:數據庫

如上圖所示:這是一個分佈式應用,完成一個付款業務的操做須要有4個微服務參與,你們都獨立運行在本身的JVM中,其中,訂單系統、商品系統和會員系統是服務提供者,有本身對應的數據庫。
  客戶端應用在發起了付款請求時,調用了訂單系統的支付業務makePayment,而makePayment方法中,又調用了遠程商品系統的decrease方法和遠程會員系統的payment方法,分別作減庫存和扣餘額的操做,那如今就有兩種狀況了:
  第一種狀況是流程正常執行,各個業務參與者都沒有異常,萬事大吉。
  第二種狀況是其中有一個業務參與者出現異常了,按照咱們上面對本地事務的理解,它們應該要作到要麼所有執行成功,要麼所有執行失敗了,這樣才能確保數據一致性。但如今這個不是單體應用,而是分佈式應用,本來一個本地邏輯執行單元被拆分到了多個獨立的微服務中,這些微服務又分別操做不一樣的數據庫和表,服務之間經過網絡調用。因此這就沒辦法確保要麼所有執行成功,要麼所有執行失敗了,由於我怎麼知道遠程的服務是否執行成功呢。
  因此,如今的問題就出現了,咱們不能再用單體應用的那種事務方式,套用在分佈式應用中了,必需要思考用什麼樣的解決方案來控制分佈式應用的事務問題。這就是「分佈式事務」。安全

分佈式事務的解決方案

分佈式事務的解決方案有不少,但能夠具體概括爲兩種:網絡

  • 強一致性事務
    強一致性事務的表明就是XA事務協議了,它由Oracle Tuxedo提出,把分散到各個JVM中的事務資源,又整合爲全局事務統一管理了。
  • 柔性事務
    而柔性事務並無像強一致性事務那樣整合爲全局事務,但其目的都是確保分佈式事務最終一致性的。柔性事務又能夠細分爲TCC事務和可靠消息事務,這兩種分佈式事務處理方式不同,接下來咱們具體分析一下這幾種分佈式事務解決方案的實現原理。

XA事務協議

XA是一個分佈式事務協議,XA中大體分爲兩部分:事務管理器和本地資源管理器。其中本地資源管理器每每由數據庫實現,好比Oracle、DB2這些商業數據庫都實現了XA接口,MySQL5.7以後也支持XA分佈式事務。而事務管理器做爲全局的調度者,負責各個本地資源的提交和回滾。
  XA分佈式事務協議的工做原理是:把各個微服務中的本地資源交給一個統一的事務管理器管理,事務管理器能夠看作是事務協調者的角色(coordinator),各個本地資源管理器能夠看作是事務參與者的角色(partcipant)。各個事務參與者之間不能直接通信,而是經過事務協調者間接通信,通俗來講,服務A怎麼知道服務B是否執行成功?就是由事務協調者轉告各個事務參與者了。經過如下分析圖,看看XA實現分佈式事務的流程:架構

以上就是XA分佈式事務執行流程,加入了全局的事務管理器做爲協調者,在接收到發起帶事務的業務方法後,發送prepare到各個事務參與者,各個事務參與者接收到prepare後,開啓本地事務being,並執行本地業務流程,若是流程正常運行,則返回ready結果給事務協調者,告知準備就緒了,這時,若是各個事務參與者返回的結果都是ready,那麼事務協調者就會再次發送一個全局事務提交global_commit的消息到各個事務參與者,最後,各個事務參與者受到global_commit後,提交本地事務commit。
  這是業務流程正常執行的狀況,那若是由於流程有異常就走以下流程:事務協調者仍是發送prepare到各個事務參與者,事務參與者接收到prepare後,開啓本地事務begin,接着執行業務流程,此時,若是有某個事務參與者執行業務報錯,返回異常abort給事務協調者,事務協調者會再次發送全局回滾global_rollback給各個事務參與者,事務參與者接受到global_rollback後,開始回滾本地事務rollback,即使是流程正常執行的也要回滾掉,這樣就能確保要麼一塊兒成功,要麼一塊兒失敗。
  總的來講,XA協議比較簡單,並且一旦商業數據庫實現了XA協議,使用分佈式事務的成本也比較低。可是,XA也有致命的缺點,那就是性能不理想,特別是在併發量很高的狀況下,會帶來性能瓶頸,由於根據以上執行流程圖的分析可知,在全局事務管理器向各個事務參與者發送prepare時,是須要鎖住資源的,也就是此時,全部相關連的微服務都處於阻塞狀態,須要等到全部事務參與者返回最終處理結構,才能釋放鎖,因此XA沒法知足高併發場景。其次,XA目前在數據庫的支持上不太理想,mysql5.7以前是不支持的,而且還有許多nosql也沒有支持XA,而大多數新型的互聯網微服務應用都會使用各類nosql數據庫,因此這就致使了XA的應用場景變得很是狹隘。併發

TCC事務

TCC是Try-Confirm-Cancel的簡稱。其核心思想是:每一個須要開啓分佈式事務的業務方法,都要註冊一個與其對應的檢測、確認和撤銷的操做,以下:oracle

  • Try階段:主要是對業務系統作檢測的操做,沒有問題就調用確認操做,有問題則調用取消操做。框架

  • Confirm階段:確認執行業務操做。異步

  • Cancel階段:取消執行業務操做。
    具體執行流程以下:

  • 經過以上的流程,咱們能夠發現,TCC的分佈式事務處理與XA的分佈式事務處理流程是很是類似的, 調用try接口檢查業務是否有異常的操做,相似於XA的prepare預提交,若是接口返回正常,則調用confirm確認執行業務,操做數據。
      那若是其中有一個事務參與者在調用了try接口檢測後,返回了異常給事務協調者,可是以前極可能已經有其餘事務參與者調用了confirm接口,執行業務流程操做了數據,那這時,事務協調者就須要調用事務參與者的cancel接口,撤銷以前修改的數據,達到相似回滾的效果。
      不過XA是在跨庫的DB層面,而TCC是應用層面,須要經過業務邏輯來實現分佈式事務。TCC的實現方式優點在於:沒有項目XA協議那樣,把分佈的資源統一管理,這就使得分佈的資源不會被加鎖,從而提升總體的吞吐量,因此這種分佈式事務的解決方案,在性能和吞吐量要求高的應用使用的仍是比較多的。而不足之處則在於對應用的侵入性很是強,業務邏輯的每一個分支都須要實現try、confirm、cancel三個操做。此外,其實現難度也比較大,須要按照網絡狀態、系統故障等不一樣的失敗緣由實現不一樣的回滾策略。同時,confirm和cancel接口還要考慮冪等性的問題,由於confirm和cancel有可能會被屢次調用。

可靠消息事務

可靠消息事務全稱叫作可靠消息事務最終一致性,它一般沒有像前面兩種分佈式事務解決方案那樣有回滾或撤銷數據的操做,而更多的是強調事務的補償和重試。這裏的可靠消息指的是消息隊列中間件,消息隊列其中一個特色就是消息可靠性,而該解決方案最終能達到事務一致,依靠的核心就是消息隊列。先粗略的看看它執行流程:

從以上的執行流程能夠發現,各個事務參與者都是相對獨立的,無論在執行業務方法的過程當中是否有異常,總體的業務流程都要先跑完,這個是該解決方案的前提。而後在調用事務參與者的業務方法的同時,往消息隊列發送事務相關的消息,這樣的話,出現異常的事務參與者再從消息隊列中獲取消息,從新執行本地的業務,達到補償和重試的效果,整個事務中,無論中間有哪些參與者出錯,可是最終仍是事務一致的。
  這種解決方案是全部解決方案中最柔性的,而且靈活度很是高,能夠根據本身具體的業務場景作改變,同時,對比TCC來講,性能和吞吐量更高,而且對應用的侵入性更低。性能的提升體如今:沒有了業務檢測的環節,跟本來同樣,該怎麼調用遠程方法就怎麼調用,只是增長了一個往MQ發送消息的操做,可是該操做是異步的,並且MQ也是具有高吞吐量的特性。而侵入性更低體如今:MQ是中間件,只須要經過網絡來調用便可,咱們的業務方法並不會因爲分佈式事務解決方案的加入而有太多的改造,加入的代碼更可能是之外圍擴展,或者組件的方式加入。
  可靠消息事務最終一致性的解決方案優勢很明顯,但缺點也不是沒有的,首先該解決方案設計過於複雜,組件不少,須要考慮的狀況繁雜,實現起來比較困難。其次,雖然它是最柔性,最靈活和性能最高的,可是事務的原子性和一致性是最弱的,由於這種解決方案是以你們都不出錯爲前提的,若是其中有一個出錯,本身經過補償機制從新執行本地事務,但重試的過程自己就是不肯定性的,好比說:A轉錢給B,A帳戶扣錢了,可是B帳戶加錢時出錯,在該機制下,A帳戶不會回滾,而是讓B帳戶從新嘗試加錢,那這就產生時間上的延遲了,極可能等了好久,都沒見B帳戶把錢加上去,或者不斷重試都是失敗的,最終致使整個事務不一致,須要人工處理。
  總的來講,可靠消息事務最終一致性因爲它的事務原子性和一致性比較弱,因此決定了它在一些事務ACID要求很是強的應用中是不能使用的,不然會形成不少安全性的問題,可是對於大多數互聯網應用來講,都是一個不錯的解決方案。而具體使用哪種,仍是取決於項目的業務場景,而後作全面的對比、考量和取捨。

感興趣的能夠本身來個人Java架構羣,能夠獲取免費的學習資料,羣號:855801563對Java技術,架構技術感興趣的同窗,歡迎加羣,一塊兒學習,相互討論。

相關文章
相關標籤/搜索