從0開始寫框架(二)—分布式事務解決方案

本文作者:羅海鵬,叩丁狼高級講師。原創(chuàng)文章,轉載請注明出處。

前言

??工欲善其事必先利其器,既然我們決定要做一個分布式事務框架,那首先需要了解一下,分布式事務是怎么回事,它跟傳統(tǒng)的本地事務有什么區(qū)別,解決方案有哪些,每種解決方案的對比等等。

本地事務

??在了解分布式事務之前,先回顧一下本地事務,顧名思義,本地事務就是在同一個JVM中,一個開啟了事務的業(yè)務方法就是本地事務。而這一個開啟了事務的業(yè)務方法里面的操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗,不允許只成功一半另外一半執(zhí)行失敗的事情發(fā)生。例如該業(yè)務方法中,有兩次數(shù)據(jù)庫更新操作,那么這兩次數(shù)據(jù)庫操作要么全部執(zhí)行成功,要么全部回滾。使用專業(yè)術語來講的話,就是事務的4個基本特性:Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durablity(持久性),統(tǒng)稱ACID,這里簡單的對ACID做一個概念的說明,當作是做個筆記:

  • Atomicity(原子性)
    是指事務的操作如果成功就必須要完全應用到數(shù)據(jù)庫,如果操作失敗則不能對數(shù)據(jù)庫有任何影響。通俗的說,就是所有操作要么全部成功,要么全部失敗回滾。
  • Consistency(一致性)
    是指事務執(zhí)行前后,數(shù)據(jù)從一個狀態(tài)到另一個狀態(tài)必須是一致的,比如A向B轉賬(A、B的總金額就是一個一致性狀態(tài)),不可能出現(xiàn)A扣了錢,B卻沒收到的情況發(fā)生。
  • Isolation(隔離性)
    是指當多個用戶并發(fā)訪問數(shù)據(jù)庫時,比如操作同一張表時,數(shù)據(jù)庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個并發(fā)事務之間要相互隔離。這里涉及到數(shù)據(jù)庫的隔離級別的概念,不是我們討論的主題,不詳細展開,大家可以自行查閱相關資料。
  • Durablity(持久性)
    是指一個事務一旦被提交了,那么對數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會丟失提交事務的操作。

分布式事務

??通過以上的回顧我們知道,本地事務對于我們來說不是什么問題,因為我們可以直接使用數(shù)據(jù)庫的事務支持,比如mysql、oracle這些數(shù)據(jù)庫對事務都有很好的支持。但是,對于分布式應用來說的話,事務就沒有那么簡單了,因為需要開啟事務的業(yè)務方法,很可能是分布在不同應用程序中,這就說明,大家不在同一個JVM中,事務空間都不一樣了,那就沒辦法做到要么全部執(zhí)行成功,要么全部執(zhí)行失敗了,我們可以看以下分析圖:


??如上圖所示:這是一個分布式應用,完成一個付款業(yè)務的操作需要有4個微服務參與,大家都獨立運行在自己的JVM中,其中,訂單系統(tǒng)、商品系統(tǒng)和會員系統(tǒng)是服務提供者,有自己對應的數(shù)據(jù)庫。
??客戶端應用在發(fā)起了付款請求時,調用了訂單系統(tǒng)的支付業(yè)務makePayment,而makePayment方法中,又調用了遠程商品系統(tǒng)的decrease方法和遠程會員系統(tǒng)的payment方法,分別做減庫存和扣余額的操作,那現(xiàn)在就有兩種情況了:
??第一種情況是流程正常執(zhí)行,各個業(yè)務參與者都沒有異常,萬事大吉。
??第二種情況是其中有一個業(yè)務參與者出現(xiàn)異常了,按照我們上面對本地事務的理解,它們應該要做到要么全部執(zhí)行成功,要么全部執(zhí)行失敗了,這樣才能確保數(shù)據(jù)一致性。但現(xiàn)在這個不是單體應用,而是分布式應用,原本一個本地邏輯執(zhí)行單元被拆分到了多個獨立的微服務中,這些微服務又分別操作不同的數(shù)據(jù)庫和表,服務之間通過網(wǎng)絡調用。所以這就沒辦法確保要么全部執(zhí)行成功,要么全部執(zhí)行失敗了,因為我怎么知道遠程的服務是否執(zhí)行成功呢。
??所以,現(xiàn)在的問題就出現(xiàn)了,我們不能再用單體應用的那種事務方式,套用在分布式應用中了,必須要思考用什么樣的解決方案來控制分布式應用的事務問題。這就是“分布式事務”。

分布式事務的解決方案

分布式事務的解決方案有很多,但可以具體歸納為兩種:

  • 強一致性事務
    強一致性事務的代表就是XA事務協(xié)議了,它由Oracle Tuxedo提出,把分散到各個JVM中的事務資源,又整合為全局事務統(tǒng)一管理了。
  • 柔性事務
    而柔性事務并沒有像強一致性事務那樣整合為全局事務,但其目的都是確保分布式事務最終一致性的。柔性事務又可以細分為TCC事務和可靠消息事務,這兩種分布式事務處理方式不一樣,接下來我們具體分析一下這幾種分布式事務解決方案的實現(xiàn)原理。

XA事務協(xié)議

??XA是一個分布式事務協(xié)議,XA中大致分為兩部分:事務管理器和本地資源管理器。其中本地資源管理器往往由數(shù)據(jù)庫實現(xiàn),比如Oracle、DB2這些商業(yè)數(shù)據(jù)庫都實現(xiàn)了XA接口,MySQL5.7之后也支持XA分布式事務。而事務管理器作為全局的調度者,負責各個本地資源的提交和回滾。
??XA分布式事務協(xié)議的工作原理是:把各個微服務中的本地資源交給一個統(tǒng)一的事務管理器管理,事務管理器可以看做是事務協(xié)調者的角色(coordinator),各個本地資源管理器可以看做是事務參與者的角色(partcipant)。各個事務參與者之間不能直接通訊,而是通過事務協(xié)調者間接通訊,通俗來說,服務A怎么知道服務B是否執(zhí)行成功?就是由事務協(xié)調者轉告各個事務參與者了。通過以下分析圖,看看XA實現(xiàn)分布式事務的流程:



??以上就是XA分布式事務執(zhí)行流程,加入了全局的事務管理器作為協(xié)調者,在接收到發(fā)起帶事務的業(yè)務方法后,發(fā)送prepare到各個事務參與者,各個事務參與者接收到prepare后,開啟本地事務being,并執(zhí)行本地業(yè)務流程,如果流程正常運行,則返回ready結果給事務協(xié)調者,告知準備就緒了,這時,如果各個事務參與者返回的結果都是ready,那么事務協(xié)調者就會再次發(fā)送一個全局事務提交global_commit的消息到各個事務參與者,最后,各個事務參與者受到global_commit后,提交本地事務commit。
??這是業(yè)務流程正常執(zhí)行的情況,那如果因為流程有異常就走如下流程:事務協(xié)調者還是發(fā)送prepare到各個事務參與者,事務參與者接收到prepare后,開啟本地事務begin,接著執(zhí)行業(yè)務流程,此時,如果有某個事務參與者執(zhí)行業(yè)務報錯,返回異常abort給事務協(xié)調者,事務協(xié)調者會再次發(fā)送全局回滾global_rollback給各個事務參與者,事務參與者接受到global_rollback后,開始回滾本地事務rollback,即便是流程正常執(zhí)行的也要回滾掉,這樣就能確保要么一起成功,要么一起失敗。
??總的來說,XA協(xié)議比較簡單,而且一旦商業(yè)數(shù)據(jù)庫實現(xiàn)了XA協(xié)議,使用分布式事務的成本也比較低。但是,XA也有致命的缺點,那就是性能不理想,特別是在并發(fā)量很高的情況下,會帶來性能瓶頸,因為根據(jù)以上執(zhí)行流程圖的分析可知,在全局事務管理器向各個事務參與者發(fā)送prepare時,是需要鎖住資源的,也就是此時,所有相關連的微服務都處于阻塞狀態(tài),需要等到所有事務參與者返回最終處理結構,才能釋放鎖,所以XA無法滿足高并發(fā)場景。其次,XA目前在數(shù)據(jù)庫的支持上不太理想,mysql5.7之前是不支持的,并且還有許多nosql也沒有支持XA,而大多數(shù)新型的互聯(lián)網(wǎng)微服務應用都會使用各種nosql數(shù)據(jù)庫,所以這就導致了XA的應用場景變得非常狹隘。

TCC事務

TCC是Try-Confirm-Cancel的簡稱。其核心思想是:每個需要開啟分布式事務的業(yè)務方法,都要注冊一個與其對應的檢測、確認和撤銷的操作,如下:

  • Try階段:主要是對業(yè)務系統(tǒng)做檢測的操作,沒有問題就調用確認操作,有問題則調用取消操作。
  • Confirm階段:確認執(zhí)行業(yè)務操作。
  • Cancel階段:取消執(zhí)行業(yè)務操作。
    具體執(zhí)行流程如下:


    ??通過以上的流程,我們可以發(fā)現(xiàn),TCC的分布式事務處理與XA的分布式事務處理流程是非常相似的, 調用try接口檢查業(yè)務是否有異常的操作,類似于XA的prepare預提交,如果接口返回正常,則調用confirm確認執(zhí)行業(yè)務,操作數(shù)據(jù)。
    ??那如果其中有一個事務參與者在調用了try接口檢測后,返回了異常給事務協(xié)調者,但是之前很可能已經(jīng)有其他事務參與者調用了confirm接口,執(zhí)行業(yè)務流程操作了數(shù)據(jù),那這時,事務協(xié)調者就需要調用事務參與者的cancel接口,撤銷之前修改的數(shù)據(jù),達到類似回滾的效果。
    ??不過XA是在跨庫的DB層面,而TCC是應用層面,需要通過業(yè)務邏輯來實現(xiàn)分布式事務。TCC的實現(xiàn)方式優(yōu)勢在于:沒有項目XA協(xié)議那樣,把分布的資源統(tǒng)一管理,這就使得分布的資源不會被加鎖,從而提高整體的吞吐量,所以這種分布式事務的解決方案,在性能和吞吐量要求高的應用使用的還是比較多的。而不足之處則在于對應用的侵入性非常強,業(yè)務邏輯的每個分支都需要實現(xiàn)try、confirm、cancel三個操作。此外,其實現(xiàn)難度也比較大,需要按照網(wǎng)絡狀態(tài)、系統(tǒng)故障等不同的失敗原因實現(xiàn)不同的回滾策略。同時,confirm和cancel接口還要考慮冪等性的問題,因為confirm和cancel有可能會被多次調用。

可靠消息事務

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


??從以上的執(zhí)行流程可以發(fā)現(xiàn),各個事務參與者都是相對獨立的,不管在執(zhí)行業(yè)務方法的過程中是否有異常,整體的業(yè)務流程都要先跑完,這個是該解決方案的前提。然后在調用事務參與者的業(yè)務方法的同時,往消息隊列發(fā)送事務相關的消息,這樣的話,出現(xiàn)異常的事務參與者再從消息隊列中獲取消息,重新執(zhí)行本地的業(yè)務,達到補償和重試的效果,整個事務中,不管中間有哪些參與者出錯,但是最終還是事務一致的。
??這種解決方案是所有解決方案中最柔性的,并且靈活度非常高,可以根據(jù)自己具體的業(yè)務場景做改變,同時,對比TCC來說,性能和吞吐量更高,并且對應用的侵入性更低。性能的提高體現(xiàn)在:沒有了業(yè)務檢測的環(huán)節(jié),跟原本一樣,該怎么調用遠程方法就怎么調用,只是增加了一個往MQ發(fā)送消息的操作,但是該操作是異步的,而且MQ也是具備高吞吐量的特性。而侵入性更低體現(xiàn)在:MQ是中間件,只需要通過網(wǎng)絡來調用即可,我們的業(yè)務方法并不會由于分布式事務解決方案的加入而有太多的改造,加入的代碼更多是以外圍擴展,或者組件的方式加入。
??可靠消息事務最終一致性的解決方案優(yōu)點很明顯,但缺點也不是沒有的,首先該解決方案設計過于復雜,組件很多,需要考慮的情況繁雜,實現(xiàn)起來比較困難。其次,雖然它是最柔性,最靈活和性能最高的,但是事務的原子性和一致性是最弱的,因為這種解決方案是以大家都不出錯為前提的,如果其中有一個出錯,自己通過補償機制重新執(zhí)行本地事務,但重試的過程本身就是不確定性的,比如說:A轉錢給B,A賬戶扣錢了,但是B賬戶加錢時出錯,在該機制下,A賬戶不會回滾,而是讓B賬戶重新嘗試加錢,那這就產生時間上的延遲了,很可能等了很久,都沒見B賬戶把錢加上去,或者不斷重試都是失敗的,最終導致整個事務不一致,需要人工處理。
??總的來說,可靠消息事務最終一致性由于它的事務原子性和一致性比較弱,所以決定了它在一些事務ACID要求非常強的應用中是不能使用的,否則會造成很多安全性的問題,但是對于大多數(shù)互聯(lián)網(wǎng)應用來說,都是一個不錯的解決方案。而具體使用哪一種,還是取決于項目的業(yè)務場景,然后做全面的對比、考量和取舍。

想獲取更多技術干貨,請前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容