分布式事务:两段式提交(最终一致性)
【MySQL如何實現(xiàn)分布式事務(wù)?】
http://www.linuxidc.com/Linux/2013-10/91925.htm?
Innodb存儲引擎支持XA事務(wù),通過XA事務(wù)可以支持分布式事務(wù)的實現(xiàn)。分布式事務(wù)指的是允許多個獨立的事務(wù)資源(transac tional resources)參與一個全局的事務(wù)中。事務(wù)資源通常是關(guān)系型數(shù)據(jù)庫系統(tǒng),也可以是其它類型的資源。
?
全局事務(wù)要求在其中所有參與的事務(wù)要么全部提交,要么全部回滾,這對于事務(wù)原有的ACID要求又有了提高。另外,在使用分布式事務(wù)時候,InnoDB存儲引擎的事務(wù)隔離級別必須設(shè)置成serialiable。
?
XA事務(wù)允許不同數(shù)據(jù)庫之間的分布式事務(wù),如:一臺服務(wù)器是mysql數(shù)據(jù)庫,一臺是Oracle的,又有可能還有一臺是sqlserver的,只要參與全局事務(wù)中的每個節(jié)點都支持XA事務(wù)。
分布式事務(wù)可能在銀行系統(tǒng)的轉(zhuǎn)帳中比較常見,如一個用戶需要從上海轉(zhuǎn)1000元到北京的一個用戶賬號上面:
#?bank?ofshanghai:Update?user_account?set?money=money?–?10000?where?user=’xiaozhang’;
#?bank?ofBeijing:Update?user_account?set?money=?money?+?10000?where?user=’xiaoli’;
?
分布式事務(wù)使用兩段式提交(two-phase commit)的方式。
? ? ? ? 第一個階段,所有參與全局事務(wù)的節(jié)點都開始準備,告訴事務(wù)管理器它們準備好提交了。
? ? ? ? 第二個階段,事務(wù)管理器告訴資源管理器執(zhí)行rollback或者commit,如果任何一個節(jié)點顯示不能commit,那么所有的節(jié)點就得全部rollback。
?
當前的java的JTA(java transaction API)可以很好的支持mysql的分布式事務(wù),可以仔細參考JTA手冊。
下面的例子顯示了如何使用jta支持調(diào)用mysql分布式事務(wù)。
?
?
分布式事務(wù)處理(? Distributed Transaction Processing? ,? DTP? )涉及多個分布在不同地方的數(shù)據(jù)庫,但對數(shù)據(jù)庫的操作必須全部被提交或者回滾。只要任一數(shù)據(jù)庫操作時失敗,所有參與事務(wù)的數(shù)據(jù)庫都需要回滾。
Open? 組織定義的分布式事務(wù)處理模型X/Open DTP? 模型(1994)包括應(yīng)用程序(? AP? )、事務(wù)管理器(? TM? )、資源管理器(? RM ,即數(shù)據(jù)庫 )、通信資源管理器(? CRM? )四部分。而 XA 是 X/Open DTP 定義的事務(wù)管理器與數(shù)據(jù)庫之間的接口規(guī)范(即接口函數(shù)),事務(wù)管理器用它來通知數(shù)據(jù)庫事務(wù)的開始、結(jié)束以及提交、回滾等。
?XA? 接口規(guī)范?使用兩階段提交協(xié)議來完成一個全局事務(wù),保證同一事務(wù)中所有數(shù)據(jù)庫同時成功或者回滾。
兩階段提交協(xié)議假設(shè)每個數(shù)據(jù)庫點都存在一個?write-ahead log,每一次的write請求都是?先記log后才真正執(zhí)行寫入。
第一階段為提交請求階段(Commit-request phase):?
?? 1. 事務(wù)管理器給所有數(shù)據(jù)庫發(fā)query to commit消息請求,然后開始等待回應(yīng);?
?? 2. 數(shù)據(jù)庫如果可以提交屬于自己的事務(wù)分支,則將自己在該事務(wù)分支中所做的操作固定記錄下來(在undo log和redo log中各記一項);?
?? 3. 數(shù)據(jù)庫都回應(yīng)是否同意提交的應(yīng)答。
第二階段為提交階段(Commit phase):?
如果事務(wù)管理器收到的所有回應(yīng)都是agreement,?
?? 1. 事務(wù)管理器記日志并給所有數(shù)據(jù)庫發(fā)commit消息請求;?
?? 2. 各個數(shù)據(jù)庫執(zhí)行操作,釋放所有該事務(wù)相關(guān)的鎖和資源;?
?? 3. 各個數(shù)據(jù)庫給事務(wù)管理器回復;?
?? 4.當收到所有回復,事務(wù)管理器結(jié)束當前事務(wù)
如果事務(wù)管理器收到的任一回應(yīng)是abort,?
?? 1. 事務(wù)管理器記日志并給所有數(shù)據(jù)庫發(fā)rollback消息請求;?
?? 2. 各個數(shù)據(jù)庫執(zhí)行undo操作,釋放所有該事務(wù)相關(guān)的鎖和資源;?
?? 3. 各個數(shù)據(jù)庫給事務(wù)管理器回復;?
?? 4.當收到所有回復,事務(wù)管理器結(jié)束當前事務(wù)
兩階段提交協(xié)議的問題在于數(shù)據(jù)庫在提交請求階段應(yīng)答后對很多資源處于鎖定狀態(tài),要等到事務(wù)管理器收集齊所有數(shù)據(jù)庫的應(yīng)答后,才能發(fā)commit或者rollback消息結(jié)束這種鎖定。鎖定時間的長度是由最慢的一個數(shù)據(jù)庫制約,如果數(shù)據(jù)庫一直沒有應(yīng)答,所有其他庫也需要無休止的鎖并等待。并且,如果事務(wù)管理器出現(xiàn)故障,被鎖定的資源將長時間處于鎖定狀態(tài)。無論是任一數(shù)據(jù)庫或者事務(wù)管理器故障,其他數(shù)據(jù)庫都需要永久鎖定或者至少長時間鎖定。并且,分布式系統(tǒng)中節(jié)點越多,存在緩慢網(wǎng)絡(luò)或者故障節(jié)點的概率也就越大,資源被長時間鎖定的概率指數(shù)上升。
兩階段提交協(xié)議的另一個問題是只要有任意一個數(shù)據(jù)庫不可用都會導致事務(wù)失敗,這導致事務(wù)更傾向于失敗。對于多個副本的備份系統(tǒng),很多時候我們希望部分副本點失效時系統(tǒng)仍然可用,使用該協(xié)議則不能實現(xiàn)。并且,分布式系統(tǒng)中節(jié)點越多,存在故障節(jié)點的概率也就越大,系統(tǒng)的可用性指數(shù)下降。
另外,如果數(shù)據(jù)庫在第一階段應(yīng)答后到第二階段正式提交前的某個階段網(wǎng)絡(luò)故障或者節(jié)點故障,該協(xié)議無法提交或回滾,數(shù)據(jù)不一致不能絕對避免。
?
兩階段提交協(xié)議。
階段一:開始向事務(wù)涉及到的全部資源發(fā)送提交前信息。此時,事務(wù)涉及到的資源還有最后一次機會來異常結(jié)束事務(wù)。如果任意一個資源決定異常結(jié)束事務(wù),則整個事務(wù)取消,不會進行資源的更新。否則,事務(wù)將正常執(zhí)行,除非發(fā)生災(zāi)難性的失敗。為了防止會發(fā)生災(zāi)難性的失敗,所有資源的更新都會寫入到日志中。這些日志是永久性的,因此,這些日志會幸免遇難并且在失敗之后可以重新對所有資源進行更新。 階段二:只在階段一沒有異常結(jié)束的時候才會發(fā)生。此時,所有能被定位和單獨控制的資源管理器都將開始執(zhí)行真正的數(shù)據(jù)更新。 在分布式事務(wù)兩階段提交協(xié)議中,有一個?主事務(wù)管理器負責充當?分布式事務(wù)協(xié)調(diào)器的角色。 事務(wù)協(xié)調(diào)器負責整個事務(wù)并使之與網(wǎng)絡(luò)中的其他事務(wù)管理器協(xié)同工作。 為了實現(xiàn)分布式事務(wù),必須使用一種協(xié)議在分布式事務(wù)的各個參與者之間傳遞事務(wù)上下文信息,IIOP便是這種協(xié)議。這就要求不同開發(fā)商開發(fā)的事務(wù)參與者必須支持一種標準協(xié)議,才能實現(xiàn)分布式的事務(wù)。事務(wù)就是一個會話過程中,對上下文的影響是一致的,要么所有的更改都做了,要么所有的更變都撤銷掉。就要么生,要么死。沒有半死不死的中間不可預期狀態(tài)。 參考下薛定諤的貓。 事務(wù)是為了保障業(yè)務(wù)數(shù)據(jù)的完整性和準確性的。 分布式事務(wù),常見的兩個處理辦法就是兩段式提交和補償。 兩段式提交典型的就是XA,有個事務(wù)協(xié)調(diào)器,告訴大家,來都準備好提交,大家回復,都準備好了,然后協(xié)調(diào)器告訴大家,一起提交,大家都提交了。 補償比較好理解,先處理業(yè)務(wù),然后定時或者回調(diào)里,檢查狀態(tài)是不是一致的,如果不一致采用某個策略,強制狀態(tài)到某個結(jié)束狀態(tài)(一般是失敗狀態(tài)),然后就世界太平了。典型的就是沖正操作。 準備好了以后,如果沒有問題,收到提交,所有人都開始提交。 這個時候,比如對數(shù)據(jù)庫來說,有redo日志的。 如果某個數(shù)據(jù)庫這時候宕機了,那么它重啟的時候,先執(zhí)行檢查,也會把上一次的這些操作都提交掉的。所以各個點的數(shù)據(jù)都是一致的。 問題 1:比如 一個業(yè)務(wù)要調(diào)用很多的服務(wù)都是寫操作,如果有其中一個寫的服務(wù)失敗了,怎么辦 ?假設(shè) 4個寫的吧,有2個寫失敗了 。 kimmking:淘寶之類的網(wǎng)站一般的做法是,如果4個都成功才算成功,那么這次提交時4個寫都設(shè)置成一個中間狀態(tài),?先容許不一致。然后4個執(zhí)行完成了以后,回調(diào)或是定時任務(wù)里檢查這4個數(shù)據(jù)是不是一致的,如果一致就全部置為成功狀態(tài),如果不一致就全部置為失敗。 復雜的業(yè)務(wù)交互過程中,不建議使用強一致性的分布式事務(wù)。解決分布式事務(wù)的最好辦法就是不考慮分布式事務(wù)。就像剛說的問題一樣,?把分布式的事務(wù)過程拆解成多個中間狀態(tài),中間狀態(tài)的東西不允許用戶直接操作,等狀態(tài)都一致成功,或者檢測到不一致的時候全部失敗掉。就解耦了這個強一致性的過程。 一般情況下準實時就成了。涉及到錢,有時候也可以這么搞。 淘寶幾s內(nèi)完整一個訂單處理,不是什么問題吧。 銀行也不是全部都強一致性。也會扎差,也會沖正。 特別是涉及到多個系統(tǒng)的時候,我們比如買機票,支付完成以后,只支付完成狀態(tài),然后返回給用戶了,我們過幾分鐘再刷新頁面,才會看到變成已出票,訂單完成狀態(tài)。 這個時候,如果我們要求所有處理,都是強一致性的,那么就完蛋了。頁面要死在那兒幾分鐘,才把這個事務(wù)處理完成,返回給用戶。 這樣就肯定涉及一個問題,支付了,但是最終出票沒出來。那就沒辦法,商量換票或退款。 淘寶的訂單改成出票失敗,給支付發(fā)消息通知退款。 慢的時候,有可能是手工出票,這時出一張票半小時都可能,如果要求都必須強一致性的話,所有處理線程都掛在哪兒,系統(tǒng)早就完蛋了。 解決分布式事務(wù)的最好辦法就是不考慮分布式事務(wù)。 拆分,大的業(yè)務(wù)流程,轉(zhuǎn)化成幾個小的業(yè)務(wù)流程,然后考慮最終一致性。 問題2:分布式事務(wù)是你們自己開發(fā)的,還是數(shù)據(jù)庫自帶的? kimmking: 1、只要一個處理邏輯能保證要么成功,要么跟什么也沒做一樣,都算是事務(wù)。數(shù)據(jù)庫事務(wù),MQ也有事務(wù)。 你自己甚至可以寫個程序生成兩個文件,要么都生成了,要么都刪掉不留痕跡,這也算是事務(wù)。 2、分布式事務(wù)這一塊有個XA規(guī)范,實現(xiàn)XA接口的事務(wù),都可以加入到一個分布式事務(wù)中,被XA容器管理起來。 3、補償?shù)霓k法,需要具體情況具體分析,沒有一個各種場合都適用的框架。
【?http://yale.iteye.com/blog/1397917?】
? ? ? ? 事務(wù)是一個很重要的概念,它必須滿足ACID特性,在單機的數(shù)據(jù)庫中,這很容易實現(xiàn)。但在分布式數(shù)據(jù)庫中,各個表分散在各臺不同的機器上,如何對這些表實施分布式的事務(wù)處理就成為一個比較困難的問題,其中兩段式提交就是解決分布式事務(wù)的一種方式。
?
?
兩段式提交設(shè)計本身的思路非常的容易理解,步驟如下:
1. 協(xié)調(diào)員服務(wù)器(協(xié)調(diào)員)發(fā)送一條投票請求消息給所有參與這次事務(wù)的服務(wù)器(參與者)。
2. 當一個參與者收到一條投票請求,它會向協(xié)調(diào)員發(fā)送一條響應(yīng)請求消息,該響應(yīng)消息包含了參與者的投票:YES 或者NO。如果參與者的消息的投票是NO,那就意味著由于某些原因,參與者不能參與這次事務(wù),等價于收到了ABORT決定,本次事務(wù)的工作到此為止。
3. 協(xié)調(diào)員收集所有參與者的響應(yīng)投票,如果所有的響應(yīng)投票都是YES,那么協(xié)調(diào)員就會做出決定:COMMIT,并且會把COMMIT消息發(fā)送給所有參與者。否則,協(xié)調(diào)員則會做出決定:ABORT,此時協(xié)調(diào)員會把ABORT消息發(fā)給那些投票為YES的那些參與者(投票為NO的參與者已經(jīng)單方面ABORT了這次事務(wù),協(xié)調(diào)員不必再發(fā)送消息給這些參與者)。發(fā)送完決定后,協(xié)調(diào)員對于本次事務(wù)的工作就此停止了。
4. 投了YES票的參與者等待著來自協(xié)調(diào)員的決定(COMMIT或者ABORT),然后根據(jù)決定做完相應(yīng)的操作,然后本次事務(wù)的工作也就此為止。
?
?
? ? ? ? 步驟1,2屬于兩段式提交的階段1,步驟3,4屬于兩段式提交的階段2。在整個過程中,參與者會存在一段不確定時間段(從它發(fā)送YES的票開始,到它收到COMMIT/ABORT的決定結(jié)束),在此時間段內(nèi),參與者的進程會被block住,它需要等待接下來的決定。而協(xié)調(diào)員則不存在任何不確定時間段,它可以繼續(xù)處理其它的事務(wù)請求,發(fā)送其它事務(wù)的投票請求,在做完COMMIT/ABORT決定之后,它可以馬上去干別的事情,無需任何等待。因為協(xié)調(diào)員的工作不具有原子性,它可以交叉地做任何事。而參與者完成的是事務(wù),具有原子性,它做出承諾后,他必須保持好事務(wù)的現(xiàn)場,避免別的事務(wù)的交叉感染,從而違反了ACID中的Isolated。
?
?
? ? ? ? 從描述來看非常簡單,很容易理解,但是請注意,在整個過程中的任何時間點,都有可能發(fā)生的各種各樣的故障,有的是鏈路故障,有的是服務(wù)器故障。如果詳細考慮這些情況,實現(xiàn)就不是這么簡單了。
? ? ? ? 先考慮第一個問題,在整個執(zhí)行的過程中,無論是參與者的進程,還是協(xié)調(diào)者的進程,他們在做下一步的處理前都必須等待消息。但是,消息可能會失敗,并不總是能夠到達。為了避免無休止的等待消息,因此需要加入Timeout?。當消息超過一定的時間還沒到來的時候,我們必須做出處理,這些處理我們稱之為Timeout-Action。當服務(wù)器或者服務(wù)器的進程(無論是協(xié)調(diào)員還是參與者)從一次失敗中恢復過來的時候,我們希望服務(wù)器的進程能夠嘗試著獲得一個和其他進程一致的決定。這很好理解, ? ?
?
? ? ? ? COMMIT/ABORT的決定已經(jīng)由協(xié)調(diào)員發(fā)出了,那么恢復的參與者進程也希望能夠得到這個決定從而參與完成該事務(wù)。當然,在參與者從失敗中恢復過來的時候,由于其它的一些可能的失敗,可能COMMIT/ABORT的決定還未能做出,此時該參與者也需要做出相應(yīng)的正確處理。因此,服務(wù)器的進程必須保存一些信息,比如是一些Log。有了這些Log,才能使得從失敗中恢復的進程能夠正確恢復事務(wù)處理。
?
Timeout-Action
進程需要在3個地方等待消息:在(2),(3),(4)步開始的地方:
在(2)步驟中,參與者進程需要等來來自協(xié)調(diào)員進程的投票請求。此時如果在等待投票請求時發(fā)生了timeout,參與者服務(wù)器就可以簡單得停止該事務(wù)的工作就可以了。
在(3)步驟中,協(xié)調(diào)員需要等待接受所有參與者回應(yīng)的YES或NO的投票,在此時,協(xié)調(diào)員還未達成任何決定,參與者也沒有提交任何數(shù)據(jù),因此協(xié)調(diào)員在Timeout發(fā)生后,只需要發(fā)送ABORT決定給所有的參與者就可以了。
在(4)步驟中,參與者p已經(jīng)投了YES票,正在等待來自協(xié)調(diào)員的COMMIT或ABORT命令。在這個時間節(jié)點上,p處在不確定時間段。因此此時,p不能在timeout的時候簡單得單方面作出決定,他需要向其他服務(wù)器做咨詢才能知道該如何處理。最簡單的終止設(shè)計可以是這樣的:p依然被block住,一直詢問等待協(xié)調(diào)員,直到p重新建立起和協(xié)調(diào)員之間的聯(lián)系。接著,協(xié)調(diào)員就會告訴p已經(jīng)作出的決定(協(xié)調(diào)員沒有不確定時間期),然后p就可以接著處理決定。
?
? ? ? ? 簡單終止協(xié)議的缺點是參與者p會被不必要得block住一段時間。比如,假如有2個參與者p和q,協(xié)調(diào)員把COMMIT/ABORT決定成功發(fā)送給q了,但是在它給p發(fā)送的決定失敗了。的確,p這時是處在不確定時期,但是q已經(jīng)不在不確定期了,如果p能夠和q通信的話,p可以從q那里得到協(xié)調(diào)員發(fā)出的決定,不必一直block等到協(xié)調(diào)員恢復。
? ? ? ? 這需要參與者能夠互相知道對方,參與者之間可以直接交換信息,不必總是通過協(xié)調(diào)員的中介。要實現(xiàn)這種自由的信息交換也并不是十分困難,協(xié)調(diào)員在發(fā)送投票請求的時候可以把所有參與者的ID列表附在投票請求消息后面發(fā)送給所有的參與者,這樣參與者p在收到投票請求后就可以直接和其他所有的參與者進行交流了。這么做也不會帶來什么副作用,在收到投票請求之前,參與者之間還是互相不認識,因此在此之前(2),(3)發(fā)生的timeout還是可以單方面得中止任務(wù)或者停止事務(wù)。這個思路就出現(xiàn)另外的一個設(shè)計-協(xié)同終止設(shè)計,設(shè)計如下:
當一個參與者p在其不確定時間段內(nèi)發(fā)生了timeout,他會依次向所有其他的進程發(fā)送一個詢問請求消息,詢問做出的決定是什么或者是否能單方面得做出一個決定(因為如果有一個被詢問的參與者已經(jīng)向協(xié)調(diào)員回復了一個NO的投票,那么詢問者自然就可以單方面得做出決定ABORT這次事務(wù),因為只要有一個參與者回復了NO,那么協(xié)調(diào)員做出的決定肯定是ABORT,無需再向協(xié)調(diào)員確認了)。在這種場景下,參與者p就被稱之為發(fā)起人,作出詢問回答的服務(wù)器進程 q就可以稱之為回應(yīng)人。那么回應(yīng)人q可能有3種情況:
1. q已經(jīng)收到了COMMIT/ABORT決定:q只需要把該決定回應(yīng)給p,然后p就可以自行處理了。
2. q還沒進行投票:q此時可以單方面做出決定,因為此時協(xié)調(diào)員已經(jīng)發(fā)生故障,此時q可以回應(yīng)ABORT給p,p就可以自己做出處理。
3. q已經(jīng)回復YES投票給協(xié)調(diào)員,處在不確定期內(nèi),也沒有收到來自協(xié)調(diào)員的決定。此時q也無法給p任何幫助。
根據(jù)這個設(shè)計,如果p發(fā)送詢問請求給q,碰巧q處在情況(1)或者(2)時,p馬上就可以達成(也就是獲得)一個決定而無需任何block。如果p能通訊的其他所有的進程都處在情況(3),那么p也會被block住,直到足夠的故障被修復使得p至少能夠和一個處在情況(1)或(2)的參與者進程q通訊。需要注意的是詢問請求可以發(fā)給所有的其他服務(wù)器進程,包括協(xié)調(diào)員進程,這樣至少可以確認協(xié)調(diào)員在沒有故障的狀態(tài)下可以回復投票請求,避免了碰巧所有其他的參與者進程都在不確定期而無法提供幫助回應(yīng)這樣的窘境。
總之,協(xié)同終止設(shè)計可以降低block的概率,但不能完全排除它。
?
恢復
一個服務(wù)器進程p剛剛從一次故障中恢復,我們希望p能夠獲得一個和其它進程們已經(jīng)達成的決定一致的決定,如果不能馬上恢復這個決定,那么至少在其它的故障被修復后能夠恢復這個決定。
當一個服務(wù)器進程p把系統(tǒng)恢復到了故障發(fā)生時現(xiàn)場保存的狀態(tài),我們來進一步考慮一下。如果p是在它發(fā)送YES投票到協(xié)調(diào)員之前就發(fā)生故障了,那么該進程就可以單方面的決定取消這次事務(wù),發(fā)送NO投票給協(xié)調(diào)員,不做任何處理。同樣,如果p是在已經(jīng)收到COMMIT/ABORT決定之后或者自己已經(jīng)作出ABORT的決定之后發(fā)生故障了,那么此時p由于已經(jīng)做出了決定,p就可以作出相應(yīng)的處理,比如說取消事務(wù)操作,或者繼續(xù)把COMMIT決定的操作執(zhí)行完畢。在這些情況下,p都能夠獨立得進行故障恢復。
但是,如果p發(fā)生故障時是處在它的不確定期時,那么它就無法在恢復時獨立得做決定了,這就是問題的復雜之處。因為它投了YES,在p故障時,可能其他的參與者全部投了YES并且協(xié)調(diào)者做出了COMMIT的決定。又或者p發(fā)生故障時,其他參與者并未全部投票YES,因此協(xié)調(diào)者作出的是ABORT的決定。此時p無法根據(jù)本地信息就能獨立得進行恢復,他需要和其他進程進行交流。在這種情況下,p所面臨的情況是和time-action的情況(3)是一樣的。(設(shè)想一下,p設(shè)置了一個非常長的timeout 時間,整個故障期間都沒有超過timeout的期限)。因此此時p也采用前面提到的終止設(shè)計來解決問題。
為了保存故障發(fā)生時的狀態(tài),每個進程都必須維護一個DT Log(Database Transaction Log)。每個進程只能訪問他自己服務(wù)器上的DT Log。假設(shè)我們采用的是協(xié)同終止設(shè)計,我們來看看如果管理這些DT log.
?
1. 當協(xié)調(diào)員發(fā)送投票請求之前或之后,它寫了一條開始兩階段記錄在DT log中。該記錄大概類似這樣:
Xml代碼????
2. 如果參與者線程發(fā)送了YES投票,那么他必須在發(fā)送投票之前寫這么YES 投票記錄在DT Log中,大概類似這樣:
Xml代碼???? 如果參與者發(fā)送了NO投票,那么它可以在發(fā)送投票之前或之后寫一條ABORT ACCEPT記錄在DT log中。
?
3. 在協(xié)調(diào)員發(fā)送COMMIT決定給所有參與者進程之前,他寫入一條COMMIT DECISION記錄。
?
4. 當協(xié)調(diào)員發(fā)送ABORT決定給所有參與者進程之前或之后,它寫入一條ABORT DECISION記錄
?
5. 參與者服務(wù)器進程在收到COMMIT/ABORT決定之后,參與者進程寫入一條COMMIT ACCEPT/ABORT ACCPET記錄。
?
對上述Log做一些說明,一旦參與者服務(wù)器進程在DT日志中寫入COMMIT ACCEPT或者ABORT ACCEPT記錄后,DM(database manager)就可以執(zhí)行commit或者abort數(shù)據(jù)庫操作。具體來講還有很多細節(jié),比如系統(tǒng)中的DT Log可能是DM Log中的一部分,因此DT Log中的COMMIT ACCEPT/ABORT ACCEPT記錄是通過本地DM的Commit/Abort子程序來實現(xiàn)的,在子程序中進行具體的操作之前,DM會寫入COMMIT ACCEPT/ABORT ACCEPT記錄到日志中去。
有了這個日志系統(tǒng),當服務(wù)器S就可以按照下面的方式進行恢復:
1>?如果S檢查DT Log發(fā)現(xiàn)了記錄,那么S就知道自己是一臺協(xié)調(diào)員。如果發(fā)現(xiàn)日志還包含了COMMIT DECISION或者ABORT DECISION日志,那就證明在故障發(fā)生之前已經(jīng)產(chǎn)生了決定,他可以選擇重新發(fā)送這些決定。如果沒有發(fā)現(xiàn)這兩條記錄中的任何一條,那么S就可以單方面得決定Abort,同時向日志中寫入ABORT DECISION記錄,并重發(fā)決定。需要注意的是,要先插入COMMIT DECISION日志,再發(fā)送COMMIT決定給各個參與者進程,這很關(guān)鍵。為什么順序這么關(guān)鍵呢?試想一下,如果發(fā)送決定消息在前,插入日志在后,那么就會有一種可能,消息COMMIT DECISION發(fā)送完了但日志還沒來得及寫入的時候服務(wù)器發(fā)生故障了,當服務(wù)器恢復之后,按照前面的邏輯,它會認為還未做出任何決定,于是又單方面的決定ABORT DECISION,這下就和實際情況沖突了,參與者就會受到兩條完全沖突的決定:ABORT DECISION和COMMIT DECISION,系統(tǒng)會無法處理。如果寫日志在前,發(fā)送消息在后,系統(tǒng)也有可能在兩個時間點之間發(fā)生故障,協(xié)調(diào)員恢復時會看見日志,因此不會做任何事或者把決定重新發(fā)送一遍,因為決定事先已經(jīng)達成,即使有可能消息還沒有發(fā)送,但至少不會做出自相矛盾的決定令參與者無法是從。
2> 如果S沒有發(fā)現(xiàn)任何記錄,S就會認為自己是一臺參與者。那么就會有三種情況:
1. DT log中包含了COMMIT ACCEPT或者ABORT ACCEPT記錄,那參與者已經(jīng)獲得了決定,那么參與者可以自己來決定,可以根據(jù)記錄來查看相應(yīng)的操作是否完成,如果還未完成可以繼續(xù)從而完成相應(yīng)操作。
2. 如果日志中沒有包含VOTE YES記錄以及任何COMMIT ACCEPT或者ABORT ACCEPT記錄,我們無法得到它當時是選擇YES還是NO。我們寫VOTE YES記錄的時間也要比發(fā)送實際消息早,盡可能早得保存決定。此時S可以單方面得決定ABORT ACCEPT。
3. 如果日志中包含VOTE YES記錄但沒有任何COMMIT ACCEPT或者ABORT ACCEPT記錄。那么參與者是在不確定期發(fā)生故障的,因此它采用終止協(xié)議來獲得決定。
?
對于一個實際的系統(tǒng)而言,系統(tǒng)需要處理的是很多的事務(wù),因此不同事務(wù)的日志是交錯得存放在DT Log里。因此每條日志記錄需要包含事務(wù)的名字。而且隨著時間的積累,事務(wù)越來越多,日志的體積也會越來越龐大。因此需要定期對日志進行垃圾回收。日志垃圾回收有2個準則:
GC1:一臺服務(wù)器不能刪除事務(wù)T的日志,直到它的RM(Recovery Manager)已經(jīng)處理完了RM-Commit(T)或者RM-Abort(T)
GC2:一臺服務(wù)器不能刪除事務(wù)T的日志,直到該服務(wù)器收到消息,所有其他服務(wù)器的RM-Commit(T)或者Rm-Abort(T)已經(jīng)處理完畢。
對于GC1,通過本地的信息很容易得到。對于GC2,則需要服務(wù)器之間能夠相互通信,你可以讓協(xié)調(diào)員來執(zhí)行GC2,或者完全分布式得由各個服務(wù)器通過相互交流完成GC2.
由于實際系統(tǒng)同時并發(fā)得處理很多事務(wù),因此在某臺服務(wù)器恢復的時候,我們還需要考慮一些細節(jié)問題。當服務(wù)器恢復時,它需要把繼續(xù)完成那些還未COMMIT或ABORT的事務(wù),這些事務(wù)在完全恢復之前都會被block住從而無法訪問數(shù)據(jù)庫這部分資源,這會造成浪費。因此解決的方法是不是在整個恢復階段一直hold住這些待恢復并且在故障之前處于不確定期被block住得事務(wù)的所有的讀寫鎖,而是把這些鎖暫時全部釋放,然后再通過重新爭取鎖的方式來和新到的事務(wù)來競爭鎖,這樣避免了在整個恢復階段所有的block資源都無法訪問。具體的流程是這樣的,服務(wù)器恢復后,先處理那些沒有被block住的事務(wù),為這些事務(wù)做出決定。然后再處那些故障前被block的事務(wù),這時候恢復程序先釋放這些事務(wù)的所有讀寫鎖,然后再與故障之后新的事務(wù)一起競爭重新請求這些讀寫鎖。一旦恢復程序先釋放了待恢復的block事務(wù)的讀寫鎖,那么這些事務(wù)所持有的數(shù)據(jù)庫資源就可以被訪問了。當然由于有競爭,原來本來可以COMMIT的事務(wù)可能由于資源競爭被ABORT掉了,但帶來的好處是吞吐量大大提高。在原來的方案中,事務(wù)的鎖可以保存在DT Log里,在競爭的方案中,鎖可以不必保存,因為服務(wù)器進程可以根據(jù)Log自行決定。
轉(zhuǎn)載于:https://www.cnblogs.com/lsx1993/p/4628830.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的分布式事务:两段式提交(最终一致性)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 亿鼎哈密瓜是哪里的
- 下一篇: nginx——location 优先级