1. 程式人生 > >paxos演算法——今生

paxos演算法——今生

Paxos

定義2.1  票:即弱化形式的鎖。它具備下面幾個性質:

  • 可重新發布:伺服器可以重新發布新票,即使前面釋出的票沒有釋放。
  • 票可以過期:客戶端用一張票來給伺服器傳送命令請求時,只有當這張票是最新的票才會被伺服器接受。

從票的性質中我們可以得出如下結論:

  • 客戶端崩潰導致死鎖的問題得到解決,因為伺服器可以釋出新票,從而不影響其他客戶端。
  • 票可以避免死鎖的問題,那麼如何實現票,可以使用計數器來實現,客戶端向伺服器獲取票的請求時,我們給計算器加1. 當客戶端下次再拿伺服器端分配的票時傳送命令請求,伺服器端可以根據該票與伺服器端的票(計數器)比對來判斷其是否過期,

演算法2.1   樸素的基於票的協議。

  階段1

1: 客戶端向所有的伺服器請求一張票

  階段2

2:  if 收到過半數伺服器的回覆  then

3:        客戶端將獲得的票和命令一起傳送每個伺服器

4:      伺服器檢查票的狀態,如果票仍然有效,則儲存命令並給客戶端一個正反饋資訊

5:else

6:         客戶端等待,並重新進入階段1

7:    end if

    階段3

8:    if 客戶端從過半數伺服器處得到了正反饋  then

9:              客戶端告訴所有的伺服器執行之前儲存的命令

10:   else

11:          客戶端等待,然後重新進入階段1

12:   end if 

該演算法是有問題的:

  • 假設客戶端  u1 是第一個獲得大多數伺服器正反饋的客戶端。但是u1很慢,在告知所有伺服器執行先前儲存的命令(c1)(演算法第9行命令)之前,這時u2更新了部分伺服器的票以及儲存了c2命令,這時,執行第9行命令時會導致資料不一致的狀態,部分伺服器執行了命令c1,部分伺服器執行了命令c2。
  • 如何解決以上的問題呢?如果在階段1中伺服器不但釋出票還發布伺服器儲存的命令,當u2更新票時,發現伺服器端已經儲存了命令c1,這時,客戶端u2可以不要求伺服器儲存命令c2,而是繼續儲存命令c1,這樣兩個客戶端都嘗試執行相同的命令c1,誰先誰後則不在重要。
  • 其次不同的伺服器可能存放不同的命令。在階段1中。客戶端需要支援哪一個命令?1.對於獲得大多數伺服器端都支援的命令,則支援改命令,否則,選擇儲存票據最新的命令。票可以使用計數器變數來代替,最新的票,則計數器值最大。
  • 每個服務端都自己生產票號的話,則最新的票號不一定是最大的。伺服器端的票號會存在重複的情況。如果由客戶端自己來生成票號,這個問題可以得到解決。
  • 我們需要全域性一致的票號,因此不能由每個伺服器自己維護一個本地的計數器來產生票號。一個巧妙的辦法是讓客戶端自己來生成票號t,然後向所有的伺服器請求編號為t的票。伺服器在收到請求後,先將t和它本地的計數器進行比較,只有t大於本地的計數器時,伺服器才會釋出票(編號為t),同時將其本地計數器的值更新為t。這樣就可以得到全域性一致的產生票號的方法。這就是下面paxos演算法所使用的方案。

演算法 2.2  Paxos

客戶端(提案者)                                                                            伺服器(接收者)

 初始化………………………………………………………………………………………………………………………………………………………………

  c ——等待執行的命令                                                                      T(max) = 0  —— 當前已釋出的最大票號

  t = 0  —— 當前嘗試的票號                                                               C = NULL   ——  當前儲存的命令

                                                                                                   T(store) = 0  ——  用來儲存命令C的票

 階段1……………………………………………………………………………………………………………………………………………………………………

 1:t = t + 1

 2: 向所有伺服器發訊息,請求得到編號為t的票

                                                                                                                3:  if t > T(max)   then 

                                                                                                                4:         T(max) = t

                                                                                                                5:          回覆ok(T(store),C)

                                                                                                                6: endif

階段2………………………………………………………………………………………………………………

 7: if 過半伺服器回覆ok  then

 8:     選擇T(store)值最大的(T(store),C)

 9:     if  T(store) > 0  then

10:             c = C

11:    endif

12:    向這些回覆了 ok的伺服器傳送訊息:propose(t,c)

13:    endif

                                                                                                                 14:  if  t = T(max).   then

                                                           15:         C = c

                             16:         T(store) = t

                             17:          回覆:success

                             18:  endif

階段3 ………………………………………………………………………………………………………………

 19:  if  過半伺服器回覆  success  then 

 20:     向每個伺服器傳送訊息:execute(c)

 21: endif 

  • 與前面演算法不同的是,這個演算法沒有明確客戶端從哪個位置可以跳轉到階段1並開始新的嘗試。實際上客戶端可以在演算法的任何位置取消當前嘗試並開始新的一輪的嘗試。
  • 在階段1 與階段2 中如果票已經過期,可以讓伺服器傳送負的反饋,這樣可以提高效能,不必等到過半伺服器傳送正反饋超時而重新嘗試。
  • 連續兩次嘗試之間的等待時間可以隨機函式確定,可以緩和不同結點之間的競爭。