分佈式事務之兩階段提交

做者:Philip A. Bernstein, Vassos Hadzilacos, Nathan Goodman. 1987html

原文:Concurrency Control and Recovery in Database Systems算法

譯者:phylips@bmy 2013-02-14數據庫

譯文:http://duanple.blog.163.com/blog/static/70971767201311810939564/安全

 

[序:歷史上,數據庫領域共產生過三位圖靈獎得主Charles Bachman,E.F.Codd和Jim Gray服務器

1961年,通用電氣公司(General Electric Co.)的Charles Bachman成功地開發出世界上第一個網狀DBMS也是第一個數據庫管理系統——集成數據存儲(Integrated DataStore IDS),奠基了網狀數據庫的基礎,並在當時獲得了普遍的發行和應用。後Charles Bachman因在數據庫方面的貢獻得到1973年圖靈獎。網絡

1970年6月,IBM聖約瑟研究實驗室的高級研究員Edgar Frank Codd在Communications of ACM上發表了A Relational Model of Data for Large Shared Data Banks。首次明確提出關係數據庫模型。此後,以前基於層次模型和網狀模型的數據庫產品很快消亡。1972年,Codd提出了關係代數和關係演算的概念, 定義了關係的並、交、投影、選擇、鏈接等各類基本運算, 爲往後成爲標準的結構化查詢語言(SQL)奠基了基礎。 後來Codd又陸續發表多篇論文,論述了範式理論和衡量關係系統的12條標準,爲關係數據庫創建了一個嚴格的數學模型。而此時網狀數據庫的標準化工做正在進行,同時有人認爲關係數據庫是一個過於理想化的模型,對它的性能表示擔心。又是出現了關係數據庫與反關係數據庫兩派(歷史老是如此類似)。後E.F.Codd得到1981年圖靈獎。架構

1976年,IBM 的研究員 Jim Gray 發表了名爲Granularity of Locks and Degrees of Consistency in a Shared DataBase的論文,正式定義了數據庫事務的概念和數據一致性的機制。後Jim Gray因在數據庫和事務處理研究和實現方面的開創性貢獻得到1998年圖靈獎。併發

 

迄今,數據庫已是一個理論研究很是成熟同時商業化也足夠高的領域了。同時,它自己所涉及的知識面也是很是廣的,這篇文章咱們將主要關注事務處理方面的提交問題。分佈式

 

上世紀70年代,在關係數據庫理論基本成熟後,各大公司在關係數據庫管理系統的實現和產品開發中遇到了一系列技術問題。主要是在數據庫的規模越來越大,數據庫的結構越來越複雜,又有越來越多的用戶共享數據庫的狀況下,如何保障數據的完整性、安全性、併發性以及故障恢復的能力,這些問題成爲數據庫產品是否能實用化並最終爲用戶接受的關鍵因素。Jim Gray在解決這些重大技術問題,使RDBMS成熟並順利進入市場的過程當中,起到了關鍵性做用。歸納地說,解決上述問題的主要技術手段和方法是:把對數據庫的操做劃分爲稱爲「事務」的基本原子單位,一個事務要麼全作,要麼全不作(即all-or-nothing原則);用戶在對數據庫發出操做請求時,須要對有關的數據「加鎖」,防止不一樣用戶的操做之間互相干擾;在事務運行過程當中,採用「日誌」記錄事務的運行狀態,以便發生故障時進行恢復;對數據庫的任何更新都採用「兩階段提交」策略。以上方法及其餘各類方法總稱爲「事務處理技術」。oop

 

首先來回顧一下有關事務處理的一些經典書籍文章。Jim Gray在1977年寫過一篇文章「Notes on Data Base Operating Systems」,總共80多頁,應該算是早期很是完善地介紹數據庫這門學科內容的教科書了,文章內容來源普遍,聚集了那個時代衆多數據庫先驅們的想法和思考。IBM的System R對該文章產生了重要影響。不少內容都是直接來源於Jim Gray與他人的討論,所以不少想法已經沒法找到明確的參考文獻。在這篇文章中,將一個數據庫系統劃分紅了以下四個主要組件:數據字典(dictionary),數據通訊(data communications),數據庫管理(database manager),事務管理(transaction management)。尤爲對事務管理中的併發控制(locking)和系統可靠性機制(recovery)進行了深刻講解。好比5.7.5節,引用的就是Jim Gray 1976年的經典論文「Granularity of Locks and Degrees of Consistency in a Shared DataBase」的內容。同時描述了大量事務處理方面的細節,以及所用到一些重要協議算法,像兩階段提交協議。固然這裏關於事務處理的大部分想法也都源自於IBM IMS開發者的實際經驗以及做者自己參與的System R的開發過程。以後在1981年的文章「The Transaction Concept:Virtues and Limitations」中,Jim Gray提出事務應具備三個屬性:Consistence,Atomicity,Durability。而ACID的叫法,則是在1983年由Theo Harder和Andreas Reuter在他們發表的文章「Principles of Transaction-Oriented Database Recovery」中首次提出,以後才被普遍使用的。順便來看一些其餘重要概念的起源,Write-Ahead-Log概念是Ron Obermark在1974年提出的,大概也是在那個時候他實現了nested-two-phase commit protocol;Earl Jenner和Steve Weick在1975年首次實現了兩階段提交協議,固然實際上它可能源於由Niko Garzado在1970年實現的某些系統,此後IMS,System R都提供了不一樣形式的實現;Paul McJones和Jim Gray參考Warren Titlemann在INTERLISP中的實現,在System R中實現了DO-UNDO-REDO 策略。此外Lampson和Sturgis在1976年的論文」 Crash Recovery in a Distributed System」中獨立提出了兩階段提交協議,Lewis, Sterns和Rosenkrantz在論文」 System Level Concurrency Control for Data Base Systems」中獨立提出了nested commit protocol。1987年,Philip A. Bernstein, Vassos Hadzilacos, Nathan Goodman的書籍「Concurrency Control and Recovery in Database Systems」,全書共300多頁,系統描述了數據庫系統中的併發控制和恢復機制。

 

1992年,C.Mohan的ARIES出世,這篇論文詳細描述了一系列關於恢復,併發和存儲管理的算法。事實上該論文中的不少想法源於C.Mohan與Don Haderle的交流,那個時候Don Haderle正擔任IBM DB2的主架構師。經過這個交流,C.Mohan意識到DB2對於某些問題的處理方式與System R有所不一樣,同時System R基於shadow page的恢復算法留下了一個開放性問題「如何在使用write-ahead logging的狀況下對記錄加鎖」。ARIES就是在這樣的背景下寫出來的。因爲這篇文章試着綜合了全部相關工做,並對那些可能成爲ARIES算法一部分的特徵進行了解釋,致使這篇文章寫出來後很是長,接近70頁,這也使得它成爲TODS歷史上惟一超過50頁限制而被接受的文章。1993年,Jim Gray和Andreas Reuter 合寫的關於事務處理的經典書籍「Transaction Processing:Concepts and Techniques」出版。

 

最後再簡要介紹下Jim Gray吧:關係數據庫領域大師,因在數據庫和事務處理研究和實現方面的開創性貢獻而得到1998年圖靈獎。美國科學院、工程院兩院院士,ACM和IEEE兩會會士。1966年在UC Berkeley拿到數學工程系學位,以後去紐約大學科朗研究所呆了一年,1967年又回到了Berkeley,僅用了一年半就拿到了博士學位,不過論文題目不是關於數據庫的,而是關於語法分析理論的。博士畢業後,先在Berkeley作了兩年博士後,繼續從事理論研究與系統開發工做,以後去了IBM。在IBM工做期間參與和主持了IMS、System R、SQL/DS、DB2等項目的開發。後任職於微軟研究院,主要關注應用數據庫技術來處理各學科的海量信息。2007年1月獨自駕船出海後失蹤。

 

起初Jim Gray是作操做系統研究的,之因此轉行研究數據庫系統,據Jim Gray本身的說法是這樣的:有一次,他上司的上司來到他辦公室,對他說「Jim啊,你看如今市場上已經出現了這麼多的操做系統,可是尚未一個像樣的網絡操做系統和數據庫系統,若是你真想在IBM作出點成績的話,研究網絡操做系統和數據庫系統是頗有前途的!」,因而Jim Gray就遵從了他的建議。其實當時Jim Gray正在作關於面向對象操做系統的研究,如今看來這個選擇也是對的,由於面向對象操做系統這個方向很快就被人們放棄了。

 

Jim Gray有個好習慣,就是常常記筆記寫備忘錄。不管什麼時候去旅行,都會寫旅行報告;不管什麼時候與人談話都會把獲得的想法作備忘錄,憑藉這些,他寫了許多文章,參加了不少國際會議,並出了名。開發System R時,同事Franco一年寫了兩萬行代碼,但Jim Gray常常花時間寫做,旅遊,一年才寫了一萬行代碼,因而老大常常會去敲他的門說「快點寫代碼!!-_-」,根據Jim Gray的回憶,整個開發過程當中他大概寫了五萬到七萬行代碼,主要是涉及併發控制,系統恢復,系統啓動,安全性管理等方面。

 

本文主要參考「Concurrency Control and Recovery in Database Systems」,重點回顧下2PC的相關知識。

]

 

背景介紹

 

事務處理的困難源於兩個方面:concurrency和failures。爲了達到高的性能,併發是必要的。而在現實中,計算機系統會面臨各類各樣的故障,操做系統可能會出錯,硬件也有可能會出錯。當這些錯誤發生時,應用程序可能會在正執行的過程當中被打斷,而這可能會產生錯誤的結果。好比用戶正在轉帳,在中間失敗可能會致使一個帳戶上的錢少了,可是另外一個帳戶卻沒有收到錢的狀況。Recovery就是要避免因故障而產生錯誤結果。因此就引出了Concurrency Control和Recovery這兩個概念。

 

對於一個解決了併發控制和恢復問題的系統來講,在用戶看來,全部程序的執行都是原子的(看起來就好像沒有其餘程序在並行執行),可靠的(看起來好像並無故障發生)。這種原子的可靠的程序執行過程就稱之爲事務。併發控制算法用來保證事務原子性地執行。

 

一個分佈式事務T有一個主節點—即發起事務的那個節點。T首先將它的操做提交給位於主節點上的TM(transaction manager),以後TM再將這些操做轉發給其餘對應的節點。好比一個Read(x)或者Write(x)操做將會被轉發給x所在的那個節點,而後由那個節點的調度器和DM進行處理,看起來就像是一個本地事務同樣。以後會再將操做結果返回給主節點上的TM。所以,除了須要對節點間的請求和響應進行路由外,在一個分佈式DBS中對讀寫操做的處理與一個集中式的系統並無什麼不一樣。

 

如今來考慮下T的Commit操做。這個操做須要轉發給哪些節點呢?不像Read(x)或者Write(x)操做那樣,只須要關注存儲了x的那個節點,Commit操做須要關注全部與事務T的處理相關的節點。所以,T的主節點上的TM須要將T中的Commit操做轉發給那些T中數據訪問涉及的全部節點。對於Abort操做也是同樣。也就是說要求單個處理操做(Commit或者Abort)必須做用在分佈式DBS的多個節點上,這是集中式與分佈式事務處理的一個根本區別。

 

這個問題比它表面上看起來的要複雜地多。僅僅讓分佈式事務主節點上的TM給其餘全部節點發送Commit操做是不夠的。這是由於TM發送了Commit操做並不意味着事務提交完成了,還要DM執行這個Commit操做。雖然TM發送了Commit操做,可是調度器有可能拒絕這個請求,同時將事務Abort。在這種狀況下,若是事務是分佈式的,那麼還須要全部相關節點也都要進行Abort。

 

集中式與分佈式事務的另外一個重要的不一樣點在於它們各自所需關注的錯誤的屬性上。在集中式系統中,錯誤都是要麼不錯要麼全錯(all-or-nothing),也就是說要麼系統正常工做事務正常處理,要麼系統出錯不會有任何事務完成。可是在分佈式系統中,可能出現部分失敗(partial failures)的狀況,某些節點正常工做可是其餘一些節點出錯了。

 

在分佈式系統中故障不必定致使嚴重問題的這種屬性爲分佈式系統提升自身可靠性創造了機會。同時這也是分佈式系統倍受推崇的一個屬性。可是不多被人提到的是,這種局部失敗的狀況正是形成分佈式系統中不少難解的問題的根源。

 

在事務處理中就有這樣的一個難解問題,即事務終止的一致性。如前所述,分佈式事務的Commit或Abort操做必需要保證在事務的數據訪問涉及到的全部節點上執行。在容許局部失敗的狀況下,這個問題變得很是複雜。

 

咱們將能夠保證這種一致性的算法稱爲原子性提交協議(ACP-atomic commitment protocol)。本章咱們的主要目標就是介紹這種能夠容忍各類錯誤的ACP協議。在具體介紹以前,咱們先詳細介紹下分佈式系統中存在的各類錯誤類型。

 

分佈式系統中的故障

 

分佈式系統由兩種組件組成:負責處理信息的節點和負責在節點間傳遞消息的通訊鏈路。一般能夠用一個圖來進行表示,圖的結點表明節點,無向邊表明雙向的通訊鏈路(如圖7-1)。

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

咱們假設圖是連通的,即任意結點間都存在一條路徑。所以任意兩個節點均可以經過它們間的一個或者是多個鏈路進行通訊。咱們將這種可讓節點間進行消息傳輸的軟硬件設備統稱爲計算機網絡。咱們不須要擔憂消息是如何被路由的,對於分佈式數據庫系統來講路由是網絡系統提供的基本服務。

 

節點故障

 

當節點發生系統故障時,進程將會異常終止同時內存內容會丟失。在這種狀況下,咱們就說節點發生故障了。當節點從故障中恢復時將會首先執行一個恢復過程,使得節點能夠達到一個一致性狀態,以後才能夠繼續正常的處理過程。

 

在這種故障模型中:站點要麼是能夠正常工做(稱它是operational的)要麼是徹底沒法工做(稱之爲down),它永遠都不會執行錯誤的動做,這種類型的行爲也被稱爲fail-stop。很明顯這有些理想化。因爲軟硬件bug的存在,計算機偶爾仍是有可能會執行不正確的動做。經過不斷的測試以及軟硬件內建的冗餘,能夠構建出一個基本上知足fail-stop的系統。咱們並不想在本文中深刻討論這些技術,咱們假設節點都是fail-stop的。本章中咱們將要討論的各個協議的正確性將會基於這個假設。

 

儘管單個節點只有兩個狀態,要麼正常工做要麼中止工做,可是不一樣節點可能處於不一樣狀態。這樣仍然會存在一種局部故障(partial failure)的狀況:某些節點正常某些節點down掉。若是全部節點都down掉的話就是total failure的狀況。

 

Partial Failure很難處理。從根本上說,這是由於正常工做的節點根本沒法肯定失敗的那些節點的狀態。在這些不肯定性解決以前,正常節點可能會被阻塞,沒法Commit或者Abort一個事務。原子性提交協議的一個重要設計目標就是要儘可能將單個節點的故障的影響最小化,使得其餘節點能夠繼續處理。

 

通訊故障

 

通訊鏈路也有可能發生故障。故障可能使得節點之間沒法通訊。可能有各類各樣的通訊故障:消息內容可能會因爲鏈路上的噪聲被破壞;鏈路可能臨時失靈,致使單個消息徹底丟失;鏈路可能會中斷一段時間,致使經過它的全部消息都丟失。

 

消息的損壞能夠經過錯誤檢測碼進行檢測,而後在接收者檢測到錯誤的時候進行重傳來進行高效地處理。因爲鏈路故障致使的消息丟失能夠經過重傳丟失的消息進行處理。同時,也能夠經過從新路由來下降鏈路丟包機率。若是消息是從節點A傳給節點B的,可是因爲鏈路斷開致使網絡沒法對消息進行傳輸,那麼網絡系統會嘗試尋找一條新的從A到B的路徑。錯誤檢測碼,消息重傳以及從新路由一般都是由計算機網絡協議提供的功能。

 

不幸的是,即便有自動的從新路由,節點和鏈路的故障仍可能致使節點間沒法通訊。當節點間的全部路徑上都包含一個有故障的節點或鏈路時就會出現這種狀況。一般稱之爲網絡分區,它會將全部節點分割爲兩個或者更多個子集,在子集內部的節點相互能夠通訊,子集之間沒法通訊。好比,圖7-2就展現了圖7-1的一個系統分區。圖中包含兩個分區{B,C}和{D,E},是由節點A和鏈路(C,D)(C,E)的故障形成的。

 

當節點恢復和鏈路修復後,那些以前沒法交換信息的節點的通訊就會恢復。好比圖7-2,若是節點A恢復或者(C,D)(C,E)中有一個修復了,那麼這兩個分區就又鏈接起來了,全部的節點就又能夠通訊了。

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

 

咱們能夠經過設計一個高度互聯的網絡來下降網絡分區發生的機率,這樣部分節點和鏈路的故障不會致使節點間的路徑所有不通。可是,構建一個高度互聯的網絡須要使用更多的組件,所以成本會更高。此外,網絡拓撲還會受到其餘因素的限制,好比地理位置和通訊媒介。所以,分區是沒法徹底避免的。

 

總之,通訊故障發生在節點A沒法與節點B沒法通訊時,儘管兩個節點都沒有down掉。通訊故障可能會引起網絡分區。若是兩個節點能夠通訊,消息就會被正確地傳輸。

 

沒法傳送的消息

 

節點和通訊故障的存在須要咱們可以處理那些不能發送的消息。消息可能會由於接收者down掉或者網絡分區的存在,而沒法發送。有兩種選擇:

1. 持久化消息。由網絡系統對消息進行存儲,在能夠發送時再發送

2. 丟失消息。網絡系統再也不嘗試重發

咱們選擇第2種方式,這也是不少設備採用的方式。第1種方式須要很是複雜的協議,它們自己很是相似於ACPs,基本上等因而將原子性提交問題擴散到了系統中另外一個部分。

 

採用第2種方式的網絡系統會嘗試通知消息的發送者消息被丟掉了。可是這樣也有其固有的不可靠性。若是節點沒法對收到的消息進行確認,網絡是沒法判斷出是這個節點根本沒有收到消息仍是它已經收到了消息只是確認時失敗了。即便它能區分出這個不一樣,對未發送消息的發送者的通知也可能致使無窮遞歸,好比若是進行通知的消息自己未能發送成功,那麼通知的發送者也須要再次被通知,就會再次產生通知消息,如此循環。所以,是不能依賴這種未發送消息的通知機制的。

 

經過超時檢測故障

 

節點故障和通訊故障均可能使得一個節點沒法與另外一個節點進行通訊。也就是說,若是節點A沒法與節點B進行通訊,那麼要麼是由於B出問題了,要麼是由於A和B分屬於不一樣的分區。一般,A沒法區分出這兩種狀況。它只是知道沒法與B進行通訊。

 

那A怎麼知道沒法與B進行通訊呢?一般都是經過超時機制來肯定的。A向B發送一個消息,而後等待一個預先肯定的時間段,稱之爲超時時間段。在此期間,若是收到了響應,那麼很明顯A和B是能夠通訊的。若是超過了這個時間段,A尚未收到響應,那麼A就認爲它沒法與B完成通訊。這個時間段必須是從A到B發送消息加上B處理消息再加上返回給A這三個過程所花費的最大可能時間。找出一個合理的超時時間並非一件簡單的事情。它依賴於不少難以量化的參數:節點和通訊鏈路的物理特性,系統負載,消息路由算法,時鐘精度等等不少因素。實踐中一般也只是選擇一個對大多數狀況都算合理的超時時間。在使用超時機制時,咱們都是假設已經定好了這樣的一個超時時間取值。事實上,即便是還沒到超時時間,節點也有可能認爲它沒法與另外一節點通訊。這種狀況的發生一般是由於時鐘不精確致使的。咱們一般將這類故障稱爲timeout failures或者performance failures。與網絡分區不一樣,這類故障可能會致使很奇怪的狀況,好比A認爲它能夠與B進行通訊,可是B卻認爲它沒法與A通訊;或者是A認爲它能夠與B通訊,同時B認爲能夠跟C通訊,可是A卻認爲它不能與C進行通訊。

 

原子性提交

 

考慮一個執行過程涉及到節點S1,S2…Sn的分佈式事務T。假設S1上的TM負責管控T的執行。在S1上的TM向S1,S2…Sn發送Commit操做以前,它必須確保每一個節點上的調度器和DM已經準備好而且能夠進行Commit。不然,T就可能在某些節點上進行了Commit,而在某些節點上進行了Abort,這樣就產生了不一致。咱們來看一下調度器和DM知足什麼樣的條件纔算是準備好而且能夠進行Commit了。

 

只要T在節點上知足了可恢復條件,那麼該節點上的調度器就容許進行Commit(T)操做。也就是說,只要其餘事務針對事務T讀取的全部值的相關寫入都提交了就能夠了。須要注意的是若是調度器產生的執行過程是不會級聯abort的,那麼上面的條件就老是成立的。在這種狀況下,由於調度器隨時均可以處理Commit(T)操做,S1的TM發送Commit操做就不須要徵求調度器的意見。

 

只要T在節點上知足了Redo規則,那麼該節點上的DM就能夠執行Commit(T)了。也就是說,該節點上全部由T寫入的value值都已經進入了可靠性存儲中—數據庫或者日誌中,取決於DM的恢復算法。若是T僅僅是向某些節點提交了讀請求,那麼它就不須要徵求這些節點的DM意見。

 

只有當獲得了來自全部節點的調度器和DM的容許後,S1上的TM才能向全部節點的調度器和DM發送Commit(T)。實際上,這就是咱們要在下一節中討論的兩階段提交協議(2PC)。爲何咱們要將這樣一個看起來很簡單的思路放到單獨的一節中討論呢?緣由是前面的這些討論並未解決節點或者通訊故障。若是說在處理中有一個或者多個節點出錯了會怎麼樣呢?若是有一個或多個消息丟失了會怎樣呢?原子性提交問題的真正難點就在於設計一個具備高度容錯性的協議。

 

爲簡化討論以及專一於原子性提交問題的本質,咱們再也不侷限於TM-調度器-DM模型。爲將原子性提交問題從事務處理的其餘概念中剝離出來,咱們假設對於每一個分佈式事務T,在執行T的每一個節點上都有一個進程。這些進程負責爲事務T實現原子性提交。咱們把在T主節點上的進程稱爲T的協調者。剩餘進程稱爲T的參與者。協調者知道全部參與者的名字,所以它能夠向它們發送消息。參與者知道協調者的名字,可是它們相互之間並不知曉。

 

須要強調的是協調者和參與者都是咱們爲了闡述的方便進行的抽象。實際實現中並不須要參與事務執行的每一個節點爲每一個事務建立一個獨立進程。一般,這樣的實現都會是很低效的,由於須要管理大量的進程。協調者和參與者進程都是一種抽象,實際中在每一個節點上能夠由單個或多個進程提供它們的功能,並且一般都是由多個事務共享的。

 

咱們還假設每一個節點都包含一個分佈式的事務日誌(DT log),協調者和參與者能夠將事務相關的信息記錄在日誌中。DT log必須保存在可靠性存儲中,由於它的內容必須不受節點故障的影響。

 

嚴格地講,原子性提交協議(ACP)是一種由協調者和參與者執行的算法,經過它來保證協調者和全部的參與者要麼是將事務提交要麼是將事務回滾。咱們能夠更精確地描述以下。每一個進程只能投兩種票:Yes或No,同時最終只能達成一個決定:Commit或Abort。ACP是一種可讓進程達成以下決定的算法:

AC1:全部達成決定的進程達成的都是相同決定

AC2:進程一旦達成決定,就不能再改變該決定

AC3:只有當全部進程都投Yes的時候,才能達成Commit決定

AC4:若是沒有故障而且全部進程投的都是Yes,那麼決定最終將會是Commit

AC5:假設執行中只包含算法設計中能夠容忍的那些故障,在執行中的任意時刻,若是全部現有故障都已修復同時在足夠長的時間內都再也不有新故障發生,那麼全部進程最終將會達成一個決定。

 

該問題的抽象形式與TM-調度器-DM模型的事務處理聯繫以下。只有當A的調度器和DM已經準備好而且能夠進行Commit,節點A上的進程才能投Yes。若是進程決定Commit(或Abort),那麼A的DM要執行Commit(或Abort)操做。在執行該操做時,節點A就像一個集中式DBS,採用第6章中的算法。實際上,處理事務的不一樣節點可使用不一樣的DM算法。

 

如今咱們再討論下這些條件。AC1是說事務終止的一致性。須要注意的是,咱們並未要求全部進程達成一個決定。這也是一個不現實的目標,由於一個進程可能在發生故障後永不恢復。咱們甚至也不要求全部正常的進程達成一個決定。這也是不現實的,儘管緣由沒有那麼顯而易見。可是,咱們確實是要求一旦全部故障被修復全部進程能達成一個決定(AC5),這個需求就將那些一旦發生故障就容許進程永遠處於未決議狀態的無心義協議排除了。

 

AC2是說節點上事務的執行結果是不可更改的。若是事務一旦提交(或abort),那麼以後它就不能再被abort(或提交)。

 

AC3是說只有當事務執行中涉及的全部節點都贊成時事務才能提交。AC4是AC3的逆的一個弱化版本。它確保了在某些狀況下必須達成Commit決定,所以就將那些老是採用選擇Abort的平凡解法的協議排除在外了。可是咱們也並不要求AC3的逆徹底成立。由於就算全部進程都投的是Yes,可是最終仍是有可能Abort的(好比發生了故障,進程投的Yes並未被接收到)。關於AC3的一個很是重要的推論是,在進程還未投Yes的狀況下,它能夠在任意時刻單方面的選擇Abort。另外一方面,一旦投了Yes它就不能再單方面地採起行動。在進程投了Yes以後到它獲取足夠的信息來肯定最終決定是啥以前,這中間的這個時間段稱爲進程的不肯定區間(uncertainty period)。當進程處在這個時間段時,咱們就說進程是不肯定的(uncertain)。在這個時間段內,進程既不知道最終決定是要Commit仍是Abort,也不能單方面地決定Abort。

 

場景1:在P處於不肯定狀態時,故障致使進程P與其餘進程不可通訊。根據不肯定區間的定義,在故障恢復以前進程沒法達到決定狀態。

 

在繼續處理以前進程必須等待故障被修復,也就是說它被阻塞了。阻塞並非咱們指望的,由於它可能致使進程等待任意長的時間。場景1代表通訊故障可能致使進程被阻塞。

 

場景2:在處於不肯定狀態時,進程P發生故障。在P恢復時,它沒法僅經過它自身決定狀態。它必須與其餘進程進行通訊以肯定決定是啥。

 

咱們將進程不須要與其餘進程進行通訊就可以進行恢復的能力稱爲獨立可恢復性(independent recovery)。這種能力很是吸引人,由於它簡單低廉。此外,若是缺少這種能力那麼在全部進程都發生故障的狀況下,會致使阻塞。好比,咱們假設p是在一個total failure中第一個恢復的進程,由於p處於不肯定狀態,所以它須要與其餘進程進行通訊以肯定狀態,可是其餘的都還down着呢,所以它就沒法與它們進行通訊,所以p就被阻塞了。

 

這兩個場景代表,在進程處於不肯定狀態時發生的故障可能致使嚴重的問題。那麼咱們是否可以設計出一種沒有不肯定階段的ACPs?不幸的是,不能。咱們有以下一些結論:

1. 若是可能發生通訊故障或者徹底故障,那麼全部的ACPs均可能會致使進程被阻塞

2. 沒有一種ACP能夠保證故障進程的獨立可恢復性

 

兩階段提交協議

 

兩階段提交協議是最簡單最流行的ACP。在沒有故障發生的狀況下,它的執行過程以下:

1. 協調者發送一個VOTE-REQ消息給全部的參與者

2. 當參與者接收到VOTE-REQ消息後,它會發送一個包含參與者投票結果的消息(YES或NO)給協調者做爲響應。若是參與者投的是No,它會決定Abort事務並中止運行

3. 協調者收集來自全部參與者的投票信息。若是全部的消息都是YES,同時協調者投的也是Yes,那麼協調者就會決定進行Commit,並向全部參與者發送COMMIT消息。不然協調者就會決定進行Abort,並向全部投Yes的參與者發送ABORT消息(那些投No的參與者已經在第2步中決定Abort了)。以後,對於這兩種狀況協調者都會中止運行

4. 每一個投Yes的參與者等待來自協調者的COMMIT或ABORT消息。收到消息後執行相應動做而後中止運行。

 

2PC的兩個階段是指投票階段(步驟1和2)和決定階段(步驟3和4)。參與者的不肯定區間始於向協調者發送YES(步驟2),終於接收到COMMIT或ABORT消息(步驟4)。協調者沒有不肯定區間,由於只要它投了票結果就肯定了,固然它投票時須要知道參與者的投票結果(步驟3)。

 

很容易能夠看出2PC知足條件AC1-AC4。不幸的是,目前爲止的描述並不知足AC5。有兩個緣由,首先在協議的不少點上,進程在繼續處理以前必需要等待消息。可是消息可能會因爲故障而沒法到達。所以,進程可能會無限等待下去。爲避免這個問題,須要使用超時機制。當進程的等待由於超時而打斷時,進程必須採起特定的動做,稱之爲超時動做。所以,爲知足AC5,必須爲協議中進程須要等待消息的地方引入合理的超時動做。

 

其次,當進程從故障中恢復時,AC5要求進程可以達成與其餘進程可能已經達成的決定相一致的決定(可能還必需要等待某些其餘故障修復以後才能達成這樣的決定)。所以,進程還必需要將某些信息存入可靠性存儲中,好比DT log中。爲知足AC5,咱們還必需要說明須要將哪些信息存入DT log以及如何在恢復時使用它們。下面咱們分別來考慮這兩個問題。

 

超時處理

 

在2PC中有以下三個須要進程等待消息的地方:在步驟2,3和4的開始階段。在步驟2中,參與者須要等待來自協調者的VOTE-REQ消息。這發生在參與者進行投票以前。因爲任何一個進程在它投Yes以前均可以單方面地決定進行Abort,所以若是參與者在等待VOTE-REQ消息時超時,它能夠簡單地決定進行Abort,而後中止運行。

 

在步驟3中,協調者須要等待來自全部參與者的YES或NO消息。在這個階段,協調者也尚未達成任何決定。此外,也沒有任何參與者已經決定要Commit。所以協調者能夠決定進行Abort,可是必需要向給它發送YES的每一個參與者發送ABORT消息。

 

在步驟4中,投了Yes的參與者p要等待來自協調者的COMMIT或ABORT消息。此時,p處於不肯定狀態。所以,與前面兩種狀況下進程能夠單方面進行決定不一樣,在這種狀況下參與者必須與其餘進程商議決定如何動做。這個商議過程須要經過執行一個terminaion protocol來完成。

 

最簡單的terminaion protocol以下:在與協調者的通訊恢復以前p始終保持阻塞。以後,協調者通知p對應的決定結果。協調者確定支持這樣作,由於它沒有不肯定區間。該terminaion protocol知足AC5,由於若是全部的故障都修復了的話,p就能與協調者通訊,而後就能達到決定狀態。

 

這種簡單的terminaion protocol缺點在於,p可能要經歷沒必要要的阻塞。好比,假設如今有兩個參與者p和q。協調者先給q發送了一個COMMIT或ABORT消息,可是在發送給p以前發生了故障。所以,儘管p是不肯定的,可是q不是。若是p能夠與q進行通訊,那麼它就能夠從q那得知最終的決定結果。並不須要一直等待着協調者的恢復。

 

可是這意味着須要參與者之間須要相互知曉,這樣它們才能相互直接通訊。可是咱們前面描述原子性提交問題時,只是說協調者認識全部參與者,參與者認識協調者,可是參與者初始時相互並不知曉。可是這也不是什麼大問題,咱們可讓協調者在發送VOTE-REQ消息時將參與者信息附加在上面,發給全部參與者。這樣在參與者接收到該消息後,相互就都知道了。事實上,在發送該消息以前它們之間也是不須要相互知曉的,由於若是參與者在接收到VOTE-REQ消息前超時了,它能夠單方面地決定進行Abort。

 

下面咱們再來介紹下cooperative terminaion protocol:參與者p若是在不肯定區間超時,它會發送一個DECISION-REQ消息給全部其餘進程,設爲q,問下q是否知道決定結果或者可否單方面地作出決定。在這個場景中,p是initiator,q是responder。有以下三種狀況:

1. q已經決定進行Commit(或Abort):q簡單地發送一個COMMIT(或ABORT)消息給p,而後p進行相應動做

2. q還未進行投票:q能夠單方面地決定進行Abort。而後它發送ABORT消息給p,p會所以決定進行ABORT

3. q已經投了Yes可是還未作決定:q也是處於不肯定狀態,所以沒法幫助p達成決定。

 

對於該協議來講,若是p能夠同某個進程q通訊而且上述1或2成立,那麼p就能夠不經阻塞地達成決定。另外一方面,若是p通訊的全部進程都是3成立,那麼p就會被阻塞。那麼p將會一直阻塞,直到故障修復的出現了某個進程q知足條件1或2爲止。至少會有一個這樣的進程,即協調者。所以這個terminaion protocol知足AC5。

 

恢復

 

考慮一個從故障中恢復的進程p,爲知足AC5,p必須可以達成一個與其餘進程相一致的決定—不必定是恢復完成後就要達成,多是在等其餘故障恢復後的某個時間段內。

 

假設p恢復時它還記得發生故障時的狀態—後面咱們會再討論如何作到這一點。若是p是在它向協調者發送YES以前發生了故障(2PC的步驟2),那麼p能夠單方面地決定進行Abort。另外,若是p是在接收到來自協調者的COMMIT或ABORT消息或者已經單方面地決定Abort以後發生的故障,那麼此時它已經完成了決定。在這些狀況下,p均可以獨立地完成恢復。

 

可是若是p是在處於不肯定區間時發生了故障,那麼恢復時就沒法僅經過自身來完成決定。由於它已經投了Yes,有可能全部其餘進程也都投了Yes,這樣在p掛掉的時候就已經決定要Commit了。可是也有可能其餘一些進程投了No或者是根本尚未投,最終決定變成是要Abort。僅僅依賴本地信息,p沒法區分出這兩種可能,所以必須與其餘進程通訊再作決定。這也從一個方面說明了爲什麼無法進行獨立地恢復(independent recovery)。

 

這種狀況就跟p等待來自協調者的COMMIT或ABORT消息而超時了的狀況是同樣的。所以p能夠經過使用terminaion protocol達成決定。須要注意的是p可能會被阻塞,由於它能夠進行通訊的那些進程也是處於不肯定狀態。

 

爲了記住故障發生時的狀態,每一個進程必須保存一些信息到節點的DT log中。固然,每一個進程只能訪問它本地的那個DT log。假設使用的是cooperative terminaion protocol,DT log的管理方式以下:

1. 當協調者發送VOTE-REQ消息時,它會在DT log中寫入一條start-2PC記錄。該記錄包含了全部參與者的標識符,同時寫入能夠發生在發送消息以前或以後。

2. 若是參與者投了Yes,它會在向協調者發送YES消息前向DT log中寫入一條記錄。該記錄包含了協調者名稱及參與者列表(由協調者經過VOTE-REQ消息提供)。若是參與者投了No,它能夠在向協調者發送NO消息以前或以後寫入一個abort記錄。

3. 在協調者向參與者發送COMMIT消息以前,它會向DT log中寫入一條commit記錄。

4. 當協調者向參與者發送ABORT消息時,它會向DT log中寫入一條abort記錄。寫入能夠發生在發送消息以前或以後。

5. 在收到COMMIT(或ABORT)消息後,參與者向DT log中寫入一條commit(或abort)記錄。

在上述討論中,在DT log中寫入一條commit(或abort)記錄實際上就表明進程決定了是Commit仍是Abort。

 

如今能夠來簡單介紹下事務提交過程是如何與事務處理的其餘活動進行交互的。一旦commit(或abort)記錄寫入了DT log,DM就能夠執行Commit(或Abort)操做了。固然這其中還有大量的細節須要考慮。好比,若是DT log是做爲DM log的一部分實現的,那麼commit(或abort)記錄的寫入就可能須要經過調用本地DM的接口來實現。一般來講,細節上如何來操做取決於本地DM採用了什麼樣的算法。

 

當節點S從故障中恢復時,分佈式事務在S上的執行取決於DT log的內容:

? 若是DT log包含一個start-2PC記錄,那麼說明S就是協調者所在節點。若是它還有commit(或abort)記錄,那麼說明在發生故障前協調者已經作出了決定。若是這兩種記錄(commit或abort)都沒有找到,那麼協調者能夠經過向DT log中插入一條abort記錄來單方面地決定進行Abort。這樣能夠工做的關鍵在於,協調者是先將commit記錄寫入DT log,而後再發送COMMIT消息的(上面的第3點)。

? 若是DT log中沒有start-2PC記錄,那麼S就是參與者節點。那麼有以下三種可能:

? DT log中包含一個commit(或abort)記錄。那麼說明在發生故障以前,參與者已經達成了決定。

? DT log中沒有yes記錄。那麼要麼是參與者是在投票前發生的故障,要麼投的是No(可是在發生故障前尚未完成abort記錄的寫入)。(這也是爲什麼yes記錄必需要在發送YES消息前寫入日誌的緣由;參考上面的第2點。)所以,它能夠單方面地經過向DT log中寫入一條abort記錄決定進行Abort。

? DT log中包含了yes記錄,可是沒有commit(或abort)記錄。那麼說明參與者是在不肯定區間內發生的故障。它能夠經過使用terminaion protocol來達成決定。回想一下,yes記錄中包含了協調者名稱以及全部的參與者,這正是terminaion protocol所須要的。

 

圖7-3和7-4給出了2PC協議和cooperative terminaion protocol的具體實現,包括了上面討論的關於超時處理和DT log相關的處理動做。算法的表達比較隨意,可是直接明瞭。咱們採用send和wait來表示進程間通訊。好比「send m to p」,m表明一個消息,p表明一個或多個進程,總的意思就是執行進程將m發送給p中的全部進程。「wait for m from p」,m表明一個或多個消息,p表明一個進程,總的意思就是執行進程一直等待,直到收到來自p的消息m。若是消息可能來自多個目標,那麼以下兩種表示方式,「wait for m from all p」表示進程會一直等待直到收到p中全部進程發送的消息,「wait for m from any p」表示進程會一直等待直到收到p中某個進程發送的消息。爲避免無限等待,能夠在「wait for」語句後面加上一個「on timeout S」進行限定,S表明某種執行語句。這意味着若是消息在一個預先定義的超時時間內還未收到,那麼就再也不繼續等待,同時語句S將會被執行,以後控制流將會繼續正常往下執行,除非S中作了一些特殊控制。若是消息在正常時間內到達,那麼S將會被忽略。

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

 

  

儘管咱們一直描述的是針對單個事務的ACPs,可是很明顯DT log將會包含來自多個事務的與原子性提交相關的記錄信息。所以,爲防止不一樣事務間記錄信息的混淆,start-2PC,yes,commit和abort記錄都會包含一些信息標識它們屬於哪一個事務。此外,也須要對DT log中的那些過時信息進行垃圾回收。在垃圾回收時,有以下兩個基本原則須要遵照:

GC1:至少要等到RM執行了RM-Commit(T)和RM-Abort(T)以後,節點才能將事務T的相關日誌記錄從它的DT log中刪除。

GC2:至少有一個節點在它收到事務T涉及的全部節點都執行了RM-Commit(T)和RM-Abort(T)的消息以前,不會將事務T的相關日誌記錄從它的DT log中刪除。

 

GC1是說與事務T的執行相關的節點,在該事務做用於該節點以前它必須牢記該事務的存在。GC2是說,與事務T的執行相關的某個節點,在該節點確認事務在全部節點上成功執行以前,必須記住事務T的狀態。若是不是這樣的話,那麼從故障中恢復的一個節點可能會沒法獲取T的狀態,同時它也永遠都不能肯定事務T的狀態,這就違背了AC5。

 

經過使用每一個節點上的本地信息就能夠確保GC1。可是GC2須要節點間通訊的支持。爲了實現GC2一般有兩種極端策略:GC2只對一個節點成立,一般都是協調者;GC2對於T的執行相關的全部節點都成立。

 

咱們關於ACP的研究都是從單個事務的角度來看的,這也隱藏了節點恢復時的一些問題。在一個節點恢復時,它必需要爲那些故障發生前還未commit或abort的全部事務完成ACP的執行。何時節點能夠恢復正常的事務處理呢?在一個集中化的DBS恢復後,在重啓過程完成以前事務是沒法被處理的,由於須要恢復那些已存儲的數據庫狀態。分佈式DBS中的節點恢復也與之相似,由於某些事務可能會被阻塞。在這種狀況下,在該恢復節點的DBS在全部被阻塞的事務被提交或abort以前也是不可訪問的。

 

避免這種問題的方法取決於所使用的調度器類型。考慮strict 2PL的狀況。當節點的恢復過程已經爲全部的未阻塞事務完成決定時,它應該通知調度器來從新獲取那些在故障發生以前由被阻塞的事務佔有的鎖。實際上,一個被阻塞的事務T只須要獲取它的寫鎖。問題在於鎖表一般是保存在內存中的,所以在系統發生故障時會丟失。爲避免丟失這些信息,負責管理T的原子性提交的進程須要將T的寫鎖也記錄到它寫入到DT log中的yes記錄中。固然若是這些信息能夠從該節點的RM維護的日誌中獲取,就不必這樣作。好比,在第6章描述的undo/redo算法,由於在T投Yes以前,全部的更新都會進入日誌。這些記錄就能夠用來肯定T的寫鎖集合。

 

針對2PC的評價

 

咱們能夠從以下幾個維度來對2PC進行評價:

Resiliency:能夠容忍哪些故障?

Blocking:進程是否有可能被阻塞?若是是,什麼狀況下會被阻塞?

時間複雜度:達成決定須要花多長時間?

消息複雜度:達成決定須要多少次消息交換?

 

前兩個維度用來衡量協議的可靠性,後兩個用來衡量協議的效率。可靠性和效率是兩個衝突的目標:任意一個均可以以另外一個爲代價來得到。協議如何選擇取決於對於特定的應用來講哪一個目標更重要。可是,不管協議如何選擇,咱們都須要盡力對無端障時的狀況進行優化—主要是提升系統的正常工做速率。

 

咱們經過對非阻塞狀況下節點達成一個決定最壞狀況下所須要的消息交換輪數進行計數,來衡量ACP的時間複雜度。輪表明了消息到達目標節點所需的最大時間。用於故障檢測的超時機制就是基於這個最大的消息延遲是已知的這樣一個假設。須要注意的是,一輪內可能會有不少消息被髮送—就看有多少個發送者-接收者對。若是一個消息必須等另外一個消息接收到以後才能發送,那麼這兩個消息就確定屬於不一樣的輪。好比,2PC中的COMMIT和YES消息就屬於不一樣的輪,由於前者必需要等接收到後者後才能發送。另外一方面,全部的VOTE-REQ消息就屬於同一輪,由於它們是併發地發送到目標節點的。對於全部的COMMIT消息來講也是這樣。用於計算輪數的一種簡單方式就是認爲同一輪中發送的消息都是同時發出的,同時具備相同的延遲。所以,每一輪都是從消息發送時開始,在消息被接收後結束。

 

使用輪來衡量時間複雜度實際上就忽略了消息處理所需的時間。這也是合理的,由於一般狀況下消息傳輸延遲都要遠遠大於消息處理延遲。可是,若是要想獲得一個更精確的時間複雜度度量,還須要將另外兩個因素考慮進來。

 

首先,如目前所知,進程必須在發送或接收特定消息時將它們記錄到DT log中。在某些狀況下,這樣的一個文件服務器是在一個本地網絡中,這種針對可靠性存儲的訪問也會引入與消息發送至關的開銷。所以針對可靠性存儲的訪問次數也會成爲影響協議的時間複雜度的重要因素。

 

其次,在某些輪中,進程是要將某個消息發送到全部其餘進程。好比,在第一輪協調者會向全部參與者發送VOTE-REQ消息。這種行爲稱爲廣播。爲了廣播一個消息,進程必須將同一消息的n個拷貝放到網絡上,n表明接收者數目。一般來講,將消息放到網絡上的時間與在網絡上傳輸的時間相比要小不少。可是若是n足夠大,那麼爲廣播所進行的準備時間也會變得很大,須要考慮在內。

 

所以,關於時間複雜度的更精確的衡量可能須要將總的輪數,訪問可靠性存儲的時間以及消息廣播時間加起來。可是,後面的分析中咱們仍是會忽略後面兩個因素,只關注輪數。

 

咱們經過協議所使用的消息總數來衡量消息複雜性。這也是合理的,由於消息自己並不長。可是若是它們很長的話,咱們也是須要將長度考慮在內的,而不能僅僅考慮個數。在咱們本章中所討論的協議的消息都是很短的,所以咱們仍是隻關注消息個數。

 

如今咱們來考察下2PC的resiliency,blocking,時間和消息複雜度。

Resiliency:2PC能夠容忍節點故障和通訊故障(不管是網絡分區仍是超時故障)。咱們前面介紹的超時動做那部分的內容自己並未對超時產生的緣由作任何假設, 經過這一點就能夠得出該結論。超時多是由節點故障,分區或者僅僅是由於僞超時致使的。

Blocking:2PC是會經歷阻塞的。若是進程在不肯定區間超時,同時能夠進行通訊的那些進程自己也是不肯定的,那麼進程就會被阻塞。實際上,即便是在只有節點故障的狀況下,2PC仍然可能會被阻塞。若是要精確計算阻塞發生的機率,必需首先要知道故障發生的機率,同時這種類型的分析也超出了本書的範圍。

時間複雜度:在沒有故障發生的狀況下,2PC須要三輪:(1)協調者廣播VOTE-REQ消息;(2)參與者回覆投票信息;(3)協調者廣播最終決定結果。若是有故障發生,可能還要額外加上terminaion protocol須要的那兩輪:第一輪用於超時的參與者發送DECISION-REQ請求,第二輪用於接收到消息的進程度過不肯定區間後進行響應。可能會有多個參與者同時調用terminaion protocol。可是不一樣的調用能夠重疊進行,所以合起來也仍是隻有兩輪。

所以,在有故障發生的狀況下那些未被阻塞或沒有發生故障的進程須要五輪才能達成決定。這與故障發生的個數無關。根據定義,一個被阻塞的進程可能會被阻塞無限長的時間。所以,爲了獲得有意義的結論,咱們在考慮時間複雜度時須要將那些被阻塞的進程排除在外。

消息複雜度:令n表明參與者數目(所以總進程數就是n+1)。在2PC的每輪中,有n個消息被髮送。所以在沒有故障的狀況下,協議將會使用3n個消息。

cooperative terminaion protocol將會被那些投了Yes可是未收到來自協調者的COMMIT或ABORT消息的全部參與者調用。假設有m個這樣的參與者。所以將會有m個進程初始化terminaion protocol的執行,每一個發送n個DECISION-REQ消息。最多有n-m+1(未處於不肯定狀態的最大進程數)個進程會響應第一個DECISION-REQ消息。收到這些響應後,將會有一個新的進程從不肯定狀態退出,所以它會向另外一個terminaion protocol執行實例發送響應消息。所以最壞狀況下,由terminaion protocol發送的消息數將是:

 

在n=m時該多項式取得最大值,也就是當全部參與者都在處於不肯定區間時超時。所以,terminaion protocol貢獻了多達n(3n+1)/2個消息,對於整個2PC協議來講就是n(3n+7)/2。

 

參考文獻

 

http://stevens0102.blogbus.com/logs/43402056.html

 

附錄

 

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

 

左一Ken Thompson、左二Butler Lampson、右二Jim Gray, 右一Niklaus Wirth

 

 

分佈式事務之兩階段提交 - 星星 - 銀河裏的星星

 

JimGray與它的同事Gianfranco Putzulo and Irving Traiger

相關文章
相關標籤/搜索