1.復制的日志
每個副本都存有記錄所有更新的數據。即使是它正從一個之前的故障中恢復數據,副本也要保證其能夠參與到寫操作中的Paxos算法,因此Megastore允許副本不按順序接受日志,這些日志將獨立的存儲在Bigtable中。圖2-25是Megastore中預寫式日志的一個典型應用場景。

當日志有不完整的前綴時我們就稱一個日志副本有“缺失”(Holes)。在圖2-25中0?99的日志位置已經被全部清除,100的日志位置被部分清除,因為每個副本都會被通知到其他副本已經不再需要這個日志。101的日志位置被全部副本接受。102的日志位置被Y獲得,這是一種有爭議的一致性。103的日志位置被副本A和C接受,副本B則留下了一個“缺失”。104的日志位置則未達到一致性,因為副本A和副本B存在爭議。
2.數據讀取
在一次Crnrent讀之前,要保證至少有一個副本上的數據是最新的,也就是說所有之 前提交到日志中的更新必須復制到該副本上并確保在該副本上生效。這個過程稱之為追趕 (Catchup )。
圖2-26是一次數據讀取過程,總的來看,該過程要經過如下幾個步驟。

1)本地查詢(Query Local)
查詢本地副本的協調者來決定這個實體組上數據是否已經是最新的。
2)發現位置(FindPosition)
確定一個最高的已經提交的日志位置,選擇一個已經在該位置上生效的副本。
(1)本地讀取(Local Read):如果本地查詢確定當前的本地副本已經是最新的,則從副本中的最高日志位置和時間戳讀取數據。這實際上就是前面提到的快速讀。
(2)多數派讀取(Majority Read):如果本地副本不是最新的(或者本地查詢或本地讀取超時),從一個副本的多數派中發現最大的日志位置,然后從中選取一個讀取。選擇—個響應最快或者最新的副本,并不一定就是本地副本。
3)追趕
一旦某個副本被選中,就采取如下方式使其追趕到已知的最大日志位置處。
(1)對于所選副本中所有不知道共識值(Consensus Value)的日志位置,從其他的副本中讀取值。對于在意的沒有任何可用的已提交的值的日志位置,將會利用Paxos算法發起一次無操作的寫。Paxos將會促使絕大多數副本達成一個共識值——可能是無操作的寫也可能是以前的一次寫操作。
(2)接下來就所有未生效的日志位置生效成上面達成的共識值,以此來達到—種分布式一致狀態。
4)驗證(Validate)
如果本地副本被選中切數據不是最新,發送一個驗證消息到協調者斷定(entitygn)up,| replica)對((entity group,replica) pair)能夠反饋所有提交的寫操作。無需等待回應,如果請求失敗,下一個操作會重試。
5)查詢數據(QueryData)
在所選的副本中利用日志位置的時間戳讀取數琚。如果所選的副本不可用了,重新選中一個替代副本,執行追趕操作,然后從中讀取數據。單個的較大査詢結果可能是從多個副本中匯聚而來。
需要指出的是,本地查詢和本地讀取是并行執行的。
3.數據寫入
執行完一次完整的讀操作之后,下一個可用的日志位置、最后一次寫操作的時間戳, 以及下一次的leader副本都知道了。在提交時刻所有的更新都被打包(Packaged)和提議 (Proposed),同時還包含一個時間戳、下一次leader提名及下一個日志位置的共識值。如果該值贏得了分布式共識,它將應用到所有的副本中。否則整個事務將中止且從讀操作重新開始。
快速讀時協調者的狀態是由寫算法來保證的。這實際上描述了這樣的一個過程:如果一次寫操作不是被所有的副本所接受,必須要將這些未接受寫操作的副本中相關的實體組從協調者中移去,這個過程稱為失效(Invalidation)。失效的過程 以保證協調者所看到的副本上數據都是接受了寫操作的最新數據。在一次寫操作被提交并準備生效之前,所有的副本必須選擇接受或者在協調者中將有關的實體組進行失效。
圖2-27是數據寫入的完整過程,具體包括如下幾個步驟。

⑴接受leaden請求leader接受值作為0號提議。這實際上就是前面介紹的快速寫方法。如果成功,跳至步驟(3)。
(2)準備:在所有的副本上使用一個比其當前所見的日志位置更高的提議號進行Paxos準備階段。將值替換成擁有最高提議號的那個值。
(3)接受:請求剩余的副本接受該值,如果大多數副本拒絕這個值,返回步驟(2)。
(4)失效:將不接受值的副本上的協調者進行失效操作。
(5)生效:將值的更新在盡可能多的副本上生效。如果選擇的值和原來提議的有沖突,返回一個沖突錯誤。
4.協調者的可用性
從上面的介紹中可以發現協調者在系統中是比較重要的,協調者的進程運行在每個數據中心。每次的寫操作中都要涉及協調者,因此協調者的故障將會導致系統的不可用。雖然在實踐中由協調者導致的系統不可用的情況很少出現,但是網絡和主機故障還是有可能導致協調者出現暫時的不可用。
Megastore使用了Chubby鎖服務,協調者在啟動的時候從數據中心獲取指定的Chubby鎖。為了處理請求,一個協調者必須持有其多數鎖。一旦因為出現問題導致它丟失了大部分鎖,協調者就會恢復到一個默認保守狀態——認為所有它所能看見的實體組都是失效的。
寫入者通過測試一個協調者是否丟失了鎖從而讓其在協調者不可用的過程中得到保護。寫入者知道在恢復之前協調者會認為自己是失效的。當一個協調者突參不可用時,這個算法需要面對一個短暫(幾十秒)的寫停頓風險——所有的寫入者必須等待協調者的 Chubby鎖過期。
除了可用性問題,對于協調者的讀寫協議必須滿足一系列的競爭條件。失效的信息總是安全的,但是生效的信息必須謹慎處理。在協調者中較早的寫操作生效和較晚的寫操作失效之間的競爭通過帶有日志位置而被保護起來。較髙位置的失效操作總是勝過較低位置的生效操作。一個位置n的失效操作和一個位置m < n的生效操作之間的競爭常常和一個沖突聯系在一起。Megastore通過一個唯一的代表協調者的序號來檢測沖突:生效操作只允許在最近一次對協調者進行的讀取操作以來序號沒有發生變化的情況下修改協調者的狀態。
在實際的應用中,以下因素能夠減輕使用協調者所帶來的問題。
(1)協調者比任何的Bigtable服務器都簡單,基本上沒有依賴,所以可用性更高。
(2)協調者簡單、均勻的工作負載讓它們能夠低成本地進行預防措施。
(3)協調者輕量的網絡傳輸允許使用高可用連接進行服務質量監控。
(4)操作者能夠在維護期或者故障期集中地讓一批協調者失效。當出現某些系統默認的監控信號時這一過程會自動進行。
(5)Chubby鎖的quorum機制能夠監測到大多數網絡問題和節點的不可用。