自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官問我:分布式事務是什么?

開發(fā) 前端 分布式
事務其實大家應該不陌生,尤其是對于程序員來說,如果你連事務都沒聽說過,沒關系,因為你遇到了聰明和才智于一體的我,事務其實就是為了處理多種混合操作,涉及到多方面業(yè)務的情景

[[403411]]

事務

事務其實大家應該不陌生,尤其是對于程序員來說,如果你連事務都沒聽說過,沒關系,因為你遇到了聰明和才智于一體的我,事務其實就是為了處理多種混合操作,涉及到多方面業(yè)務的情景

重點是事務應用的場景就是為了解決多種事務必須要么同時完成,要么同時不能完成的場景,也就是做到真正意義上的"同生共死"

嚴格意義上來說事務其實具有原子性、一致性、隔離性和持久性四種特性,也就是大家老生常談的ACID

  • 原子性(Atomicity),可以理解為一個事務內(nèi)的所有操作要么都執(zhí)行,要么都不執(zhí)行
  • 一致性(Consistency),可以理解為數(shù)據(jù)是滿足完整性約束的,也就是不會存在中間狀態(tài)的數(shù)據(jù),比如你賬上有400,我賬上有100,你給我打200塊,此時你賬上的錢應該是200,我賬上的錢應該是300,不會存在我賬上錢加了,你賬上錢沒扣的中間狀態(tài)
  • 隔離性(Isolation),指的是多個事務并發(fā)執(zhí)行的時候不會互相干擾,即一個事務內(nèi)部的數(shù)據(jù)對于其他事務來說是隔離的
  • 持久性(Durability),指的是一個事務完成了之后數(shù)據(jù)就被永遠保存下來,之后的其他操作或故障都不會對事務的結果產(chǎn)生影響

嚴格意義上來說事務其實具有原子性、一致性、隔離性和持久性四種特性,也就是大家老生常談的ACID

其實在我們印象中,應該對這個事務再熟悉不過了,大家都知道事務就是為了使得一些數(shù)據(jù)庫層面的更新操作要么全部成功,要么全部失敗。

不知道大家學過Redis沒有,如果學過Redis的其實可能會有疑問,因為Redis的事務不能保證所有操作要么都執(zhí)行,要么都不執(zhí)行,但是也叫做事務。Redis其實在官網(wǎng)就已經(jīng)說明白了,官網(wǎng)中告訴大家事務中的某個命令失敗了,之后的命令還是會被處理,Redis不會停止執(zhí)行命令,也就是意味著不會回滾

Redis解釋為什么不支持回滾

他們給出的回的就是首先如果命令出錯那就是語法的錯誤,是屬于個人的編程錯誤,而且這種情況應該被檢測出來,而不是在生產(chǎn)環(huán)境出現(xiàn),于是乎Redis為了速度更快不支持回滾操作

感覺很有道理的樣子,但是又有點不對勁

好了,這下大家都知道事務是啥了,那么我們一起來看看分布式事務吧

分布式事務

剛才說的事務都是屬于單體程序中,單機中這樣是沒問題的,通過普通的事務操作就可以來解決;當我們的系統(tǒng)逐漸變大,日益變強的同時,并發(fā)量和系統(tǒng)都隨之而增加,當涉及到多個系統(tǒng)之間的配合來完成一個事務的時候,這就比較難辦了,因為無法直接通過一個系統(tǒng)的數(shù)據(jù)庫來完成

假設現(xiàn)在有訂單系統(tǒng)、扣款系統(tǒng)、積分系統(tǒng),這是屬于三個系統(tǒng),也就是分別在不同的數(shù)據(jù)庫中,但是我需要保證三個系統(tǒng)中的服務要么全部成功、要么全部失敗,其實像這種設計到多個庫、多個系統(tǒng)之間的事務操作,也就是分布式事務了

分布式事務其實說簡單也簡單,其實就是有多個本地事務組合而成,對于分布式事務而言幾乎滿足不了ACID,其實對于單機事務大多是情況下也是無法全部滿足ACID的,否則哪里來的四種隔離級別?所以更別說分布在不同數(shù)據(jù)庫、不同系統(tǒng)之間的分布式事務了

分布式事務大致可以分為六種,但是其實這六種又可以按照三種思想來分類,接下來一起看看吧

2PC和3PC是一種強一致性事務,不過還是有數(shù)據(jù)的不一致、阻塞等風險,而且只能應用在數(shù)據(jù)庫層面;而TCC是一種補償性事務的思想,適用的范圍應該是比較廣,不過這種補償性機制一般對業(yè)務的侵入性比較大,每一個操作都需要實現(xiàn)對應的三種方法;還有一種思想就是努力實現(xiàn)最終一致性事務,有本地消息、事務消息、和最大努力通知這三種方法,都是實現(xiàn)最終一致性事務,因此適用于于一些對于時間不敏感的業(yè)務

大致了解了這三類,接下來來細細學習每一種吧

  • 2PC二階段提交:準備階段、提交階段

2PC,又叫做二階段提交,二階段指的是準備階段和提交兩個階段

二階段提交屬于一種強一致性的設計,2PC引入一個事務協(xié)調(diào)者的角色來協(xié)調(diào)管理各參與者的提交和回滾機制,我們來看下具體流程

準備階段協(xié)調(diào)者會向各個參與者發(fā)送準備的命令,這個準備其實就是準備環(huán)境,可以理解成提交之前的準備工作

同步的等待所有的資源的響應之后,就到了萬事俱備,只欠提交的狀態(tài)了

提交階段,提交階段并不一定是提交事務,也有可能是回滾事務,如果第一階段都準備成功,則第二階段的提交就是提交事務;同理如果第一階段未全部準備成功,則第二階段提交的就是回滾事務了。假設第一階段都準備成功,則協(xié)調(diào)者向所有參與者發(fā)送提交命令,然后接下來等待所有參與者都成功之后,返回事務執(zhí)行成功

假設第一階段有部分參與者返回失敗的話,那么協(xié)調(diào)者則會向所有參與者都發(fā)送回滾事務的請求,即類似上圖,向全部參與者發(fā)送回滾事務

說到這里其實有些小伙伴已經(jīng)開始有疑問了,我知道了第一階段有失敗的如何處理了,但是如果第二階段出現(xiàn)失敗了咋整呢

其實這里分了兩種情況,分別是第二階段執(zhí)行的是提交階段、第二階段執(zhí)行的是回滾操作,這兩種情況的處理方式其實是一樣的,都是屬于不斷地重試,直到重試成功;對于提交來說,可以根據(jù)業(yè)務場景,執(zhí)行一定次數(shù)的重試之后,嘗試回滾;但是對于回滾操作,總不能執(zhí)行成功操作吧

所以,如果第二階段是回滾操作有失敗,當失敗次數(shù)達到一定次數(shù)的時候,最好的方法就是人工介入了

提交流程大致也分析的差不多了,接下來一起看看細節(jié)部分,2PC可以看成同步阻塞協(xié)議,同步阻塞的等待所有參與者的第一階段都有響應之后,才會進行第二階段的操作;對于Java基礎很熟悉的小伙伴是不是很快想起來Java并發(fā)包中的一個工具類CountDownLatch,以及功能類似的CyclicBarrier,忘記的趕緊回憶下

其實2PC中對于這里的同步阻塞是有超時機制的,協(xié)調(diào)者等待參與者的響應超時的情況下,會默認失敗,然后協(xié)調(diào)者直接向所有參與者發(fā)起回滾的命令,知道這次事務失敗

上面這些都是基于參與者的角度來考慮的,那如果協(xié)調(diào)者出問題了呢

協(xié)調(diào)者如果是單點的,出現(xiàn)故障之后,可能會出現(xiàn)一些系統(tǒng)的問題,我們從流程的角度分析下:

準備階段命令未發(fā)出,協(xié)調(diào)者故障,事務還沒開始,問題不大;

準備階段命令發(fā)出了,協(xié)調(diào)者故障,事務開始了,無論參與者都是成功還是失敗,最終情況都很糟糕,因為參與者無法等到下一步的指令了,也就是卡碟了,不僅事務無法執(zhí)行,還會鎖定一些公用資源而阻塞其它系統(tǒng);準備階段命令發(fā)出,全部成功,第二階段執(zhí)行提交階段命令發(fā)出,這種情況也是不行的,因為也可能因為分區(qū)和網(wǎng)絡阻塞,某些參與者未收到提交命令,理想情況下如果參與者一次性全部收到提交命令,但是參與者有可能提交失敗,這樣還是需要重試,此時協(xié)調(diào)者掛了,也是不行

準備階段命令發(fā)出,部分失敗,第二階段回滾命令發(fā)出,其實和上面情況類似,也是會出現(xiàn)各式各樣的問題

既然單點協(xié)調(diào)者不行,那就來個多個的吧,通過選舉機制再選一個新協(xié)調(diào)者

如果都處于第一階段,其實都還好,事務還沒提交,直接都會滾就好了;如果處于第二階段,假設參與者都沒掛,此時新協(xié)調(diào)者可以向所有參與者來進一步確認他們自身的情況來推斷下一步該如何操作,如果個別參與者掛了,就比較尷尬了。比如協(xié)調(diào)者發(fā)送了回滾的命令,此時第一個參與者收到了并執(zhí)行了,然后協(xié)調(diào)者和第一個參與者都掛掉了,此時其它參與者都沒收到請求,然后新協(xié)調(diào)者來了,它詢問了其它的參與者都回答OK,但是它不知道其中第一個參與者掛了,此時要是按照全部OK來處理,直接發(fā)送提交命令,就糟糕了,這不是我們想要的結果

其實雖然2PC協(xié)議上沒說,但是在實現(xiàn)的時候我們需要靈活的讓協(xié)調(diào)者將自己發(fā)過的請求在哪些地方都記一下,也就類似于日志記錄,這樣新的協(xié)調(diào)者來的時候就不、知道此時該不該發(fā)了

即使協(xié)調(diào)者知道自己應該發(fā)提交還是回滾請求,但是在參與者也一起掛了的情況下也是沒用的,因為協(xié)調(diào)者無法知道參與者在掛之前有沒有提交事務,其實這里最靠譜的方法,就是對每一步都進行相應的日志記錄,重要的步驟最好還是強綁定日志記錄的,否則操作成功了,日志記錄失敗那也很糟糕,總之就是要考慮各種極端的情況,盡最大努力去做到每個細節(jié)都考慮到

2PC是一種盡量保證強一致性的分布式事務,因為它是同步阻塞的,而同步阻塞就意味著在某些情況下會出現(xiàn)鎖定資源的情況,而且單點一旦出現(xiàn)故障,就會造成資源鎖定的情況

以下代碼取自 <<Distributed System: Principles and Paradigms>>

  1. 協(xié)調(diào)者: 
  2.  
  3.  
  4.  
  5.     write START_2PC to local log; //開始事務 
  6.     multicast VOTE_REQUEST to all participants; //廣播通知參與者投票 
  7.     while not all votes have been collected { 
  8.         wait for any incoming vote; 
  9.         if timeout { //協(xié)調(diào)者超時 
  10.             write GLOBAL_ABORT to local log; //寫日志 
  11.             multicast GLOBAL_ABORT to all participants; //通知事務中斷 
  12.             exit; 
  13.         } 
  14.         record vote; 
  15.     }   //如果所有參與者都ok 
  16.     if all participants sent VOTE_COMMIT and coordinator votes COMMIT { 
  17.         write GLOBAL_COMMIT to local log; 
  18.         multicast GLOBAL_COMMIT to all participants; 
  19.     } else { 
  20.         write GLOBAL_ABORT to local log; 
  21.         multicast GLOBAL_ABORT to all participants; 
  22.     } 
  23.  
  24. 參與者: 
  25.  
  26.     write INIT to local log; //寫日志 
  27.     wait for VOTE_REQUEST from coordinator; 
  28.     if timeout { //等待超時 
  29.         write VOTE_ABORT to local log; 
  30.         exit; 
  31.     } 
  32.     if participant votes COMMIT { 
  33.         write VOTE_COMMIT to local log; //記錄自己的決策 
  34.         send VOTE_COMMIT to coordinator; wait for DECISION from coordinator; 
  35.         if timeout { 
  36.             multicast DECISION_REQUEST to other participants; //超時通知 
  37.             wait until DECISION is received;  /* remain blocked*/ 
  38.             write DECISION to local log; 
  39.         } 
  40.         if DECISION == GLOBAL_COMMIT 
  41.             write GLOBAL_COMMIT to local log; 
  42.         else if DECISION == GLOBAL_ABORT 
  43.             write GLOBAL_ABORT to local log; 
  44.     } else { 
  45.         write VOTE_ABORT to local log; 
  46.         send VOTE_ABORT to coordinator; 
  47.     } 
  48. 每個參與者維護一個線程處理其它參與者的DECISION_REQUEST請求: 
  49.  
  50.     while true { 
  51.         wait until any incoming DECISION_REQUEST is received; 
  52.         read most recently recorded STATE from the local log; 
  53.         if STATE == GLOBAL_COMMIT 
  54.             send GLOBAL_COMMIT to requesting participant; 
  55.         else if STATE == INIT or STATE == GLOBAL_ABORT; 
  56.             send GLOBAL_ABORT to requesting participant; 
  57.         else 
  58.             skip;  /* participant remains blocked */ 
  59.     } 
  • 3PC三階段提交:準備階段、預提交階段、提交階段

3PC其實就是2PC的升級版,相比于2PC,參與者也引入了超時機制,并且還新增了一個階段使得參與者可以利用這一階段來統(tǒng)一各自的狀態(tài)

3PC分為三個階段:準備階段、預提交階段、提交階段??雌饋砀袷前?PC中的提交階段分為了預提交和提交的兩個階段, 但是這里的準備階段其實就是詢問參與者的自身狀況,就是問你現(xiàn)在的狀況如何,負載是不是超載,還可以再接受新的任務嗎

而預提交階段其實就是類似于2PC的準備階段,就是除了事務的提交該做的都做了,就是之前的準備工作,但是在3PC中叫做預提交階段 


3PC是首先準備階段并不會直接執(zhí)行事務,而是先去詢問此時的參與者是否有條件可以執(zhí)行這個事務,因此不會直接鎖住資源,而預提交階段的引入則是為了起到了一個統(tǒng)狀態(tài)的作用,在預處理階段表面所有參與者都已經(jīng)回應了

其實這也多引入了一個階段,因此性能會差一些,而且絕大部分的情況下資源也都是沒問題的,也就是可用的,這樣等于每次明知可用但是還是得詢問一次

當然,這其中哪一個階段的參與者返回失敗都會宣布事務失敗,這個2PC也是一樣的,當然到最后的提交階段和2PC一樣都是只要是提交請求也就只能通過不斷的重試咯

我們上面說過2PC是同步阻塞的,協(xié)調(diào)者掛在了提交請求還未發(fā)出去的時候是最尷尬的,所有參與者都已經(jīng)鎖定了資源并且阻塞的等待著,于是引入了超時機制,參與者則不用直接干干的等著了,如果是等待提交命令超時,那么參與者就會提交事務了,因為到了這一階段大概率都是提交的,如果是等待預提交超時,接下來也沒啥影響

這里其實有一個問題,然后超時機制會帶來數(shù)據(jù)不一致的問題,就是在等待提交命令的時候超時,那么參與者自動提交事務了,但是呢,也可能執(zhí)行的是回滾機制,這樣一來數(shù)據(jù)便出現(xiàn)了不一致了

3PC的引入是為了解決提交階段2PC協(xié)調(diào)者和其中的部分參與者都掛了的情況下,然后之后的新選舉的協(xié)調(diào)者不知道當前應該是該提交還是回滾的問題,新協(xié)調(diào)者來的時候發(fā)現(xiàn)有一個參與者處于預提交或者提交階段,那么表明所以參與者都已經(jīng)經(jīng)過確認了,所以此時執(zhí)行的就是提交命令了

3PC就是通過引入預提交階段來是的參與者之間的狀態(tài)得到真正的統(tǒng)一,也就是留了一個階段讓大家都同步,但是這也是只能讓協(xié)調(diào)者知道如何做,并不能保證這樣做一定是對的,這其實和上面的2PC的分析一直,因為掛了的參與者到底有沒有執(zhí)行事務是無法斷定的,所以說呢,3PC通過預提交階段可以減少故障時候的復雜性,但是并不能保證數(shù)據(jù)真正的一致,處理掛了的那個參與者也恢復了

一句話總結:3PC相比于2PC做了一定的參與者超時機制的改進,并且增加了預提交階段,可以使故障恢復之后的協(xié)調(diào)者的決策復雜度降低,但整體的交互過程會變得更長,性能會有所下降,而且還會出現(xiàn)數(shù)據(jù)不一致的情況

  • TCC:Try-Confirm-Cancel

TCC屬于業(yè)務層面的分布式事務,分布式事務不僅僅包含數(shù)據(jù)庫層面的操作,還包括業(yè)務層面的操作,這時候TCC就要排上用場了

TCC指的就是Try、Confirm、Cancel三個步驟,Try指的是預留,指的是資源的預留和鎖定;Confirm指的就是確認操作,這一步其實就是屬于真正的執(zhí)行了,真正的消耗資源來進行相應的業(yè)務提交操作;Cancel指的是撤銷操作,可以理解為把預留階段的動作銷毀了,就是一個回滾操作

從思想上來看,其實是和2PC、3PC是類似的,都是先試探性的執(zhí)行,先試探性的鎖定資源,如果每一個參與者都沒問題了,就可以執(zhí)行真正的操作了,提交或者回滾 

舉個例子:一個事務要執(zhí)行A、B、C三個操作,那么先對三個操作執(zhí)行預留動作,如果所有都預留成功了那么就執(zhí)行確認提交操作,如果其中至少有一個預留失敗,那就都執(zhí)行撤銷的動作

TCC模型其中還有一個事務管理者的角色,用來記錄TCC有關的全局事務操作的狀態(tài),并且準備提交或者回滾事務,其實這個是比較容易理解的,難點在于業(yè)務上的定義

怎么說呢,TCC這種是對業(yè)務的侵入較大和業(yè)務緊耦合,需要根據(jù)相應的特定的業(yè)務場景和業(yè)務邏輯來設定的響應操作,其實還有一點需要注意的是,撤銷和確認的操作的執(zhí)行的就是需要重試,就是需要保證操作的冪等性

TCC相對來說,適用的范圍應該是更廣的,但是這個是有一個缺點的,就是這個和業(yè)務是耦合的,需要大量的開發(fā),因為都是在業(yè)務上的實現(xiàn),等同于每個場景都需要三個方法來實現(xiàn),就是嵌入業(yè)務,所以TCC是可以跨業(yè)務系統(tǒng)、跨數(shù)據(jù)庫來實現(xiàn)事務

  • 本地消息表

本地消息表,就是利用了各個系統(tǒng)的本地事務來實現(xiàn)分布式事務,這個呢,其實很簡單的道理,其實就是會有一張存放本地消息的表,一般都是放在數(shù)據(jù)庫中,然后在執(zhí)行業(yè)務的時候,必須把業(yè)務的真正的執(zhí)行操作和相應的這個操作的消息放入到消息表中這個操作,存放到同一個事務中,就是只要操作成功了,就必須保證該消息也成功的放入到本地的消息表中了

接下來調(diào)用下一個操作的時候,如果下一個操作調(diào)用成功了,就可以直接把消息的狀態(tài)改成已成功,調(diào)用失敗也沒有關系,我們可以寫一個定時任務來讀取本地的消息表,然后篩選出未執(zhí)行成功的消息再調(diào)用對應的服務,服務更新成功了,再改變消息的狀態(tài)

其實這里也是需要重試機制,重試就得保證對應服務的方法是冪等的,而且一般重試也會有最大的次數(shù),超過最大次數(shù)的時候可以人工介入

本地消息表實現(xiàn)的是業(yè)務的最終一致性,需要能夠容忍數(shù)據(jù)暫時不一致的情況

  • 消息事務

其實消息事務,最典型的就是屬于RocketMQ中的實現(xiàn)了,而且應用的場景也是比較多的

RocketMQ的機制就是先給Broker發(fā)送事務消息,也就是半消息,半消息指的是這個消息對消費者來說不可見,然后發(fā)送成功后,發(fā)送之后會繼續(xù)執(zhí)行本地事務

第二步就是根據(jù)本地事務的執(zhí)行結果向Broker發(fā)送Commit和Rollback命令,如果一直不發(fā)送,RocketMQ的發(fā)送方會提供一個反查事務狀態(tài)的接口,用來反查相應的事務的結果到底是成功還是回滾

其實這也就是個超時機制,在一段時間內(nèi)沒有收到任何的操作請求,那么Broker就會通過相應的結果查出該事務是否成功執(zhí)行呢,是Commit還是Rollback

如果是Commit,則broker就會發(fā)送這個消息到訂閱方,然后再做對應的操作,做完了之后就可以消費這個消息,如果是Rollback則訂閱方即收不到這個消息,等同于事務沒有執(zhí)行過

  • 最大努力通知

其實最大努力通知我個人認為是一種思想,像上面的本地消息表、事務消息也是屬于最大努力通知類型的

本地消息表會有后臺任務定時查看未完成的任務的消息,然后去調(diào)用對應的服務,進行多次重試,當多次失敗的時候就需要引入人工,這也是屬于最大努力

事務消息也是屬于類似,半消息被Commit之后就會發(fā)送到消費端了,如果消費端一直不消費或者消費不了則會一直重試,如果重試次數(shù)達到一定數(shù)量,該消息變回進入到私信隊列,也是屬于盡最大努力通知吧

這應該是屬于一種思想,盡最大努力的達到事務的最終一致,適用于對時間不敏感的業(yè)務場景

 

責任編輯:姜華 來源: 大魚仙人
相關推薦

2022-11-25 17:29:27

分布式事務

2024-06-26 11:55:44

2024-09-24 16:30:46

分布式鎖Redis數(shù)據(jù)中間件

2022-08-11 18:27:50

面試Redis分布式鎖

2023-01-26 02:16:17

2021-12-02 08:19:06

MVCC面試數(shù)據(jù)庫

2020-09-27 06:52:22

分布式存儲服務器

2024-02-22 17:02:09

IDUUID雪花算法

2024-08-07 08:15:47

2025-03-05 00:01:00

ReduxReact

2022-06-27 08:21:05

Seata分布式事務微服務

2021-05-20 08:54:16

Go面向對象

2020-04-16 08:22:11

HTTPS加解密協(xié)議

2010-08-23 15:06:52

發(fā)問

2022-05-24 08:03:28

InnoDBMySQL數(shù)據(jù)

2021-01-08 09:14:59

分布式事務框架

2024-03-01 09:53:34

2024-10-07 08:52:59

分布式系統(tǒng)分布式 IDID

2022-06-21 08:27:22

Seata分布式事務

2017-07-26 15:08:05

大數(shù)據(jù)分布式事務
點贊
收藏

51CTO技術棧公眾號