1. 程式人生 > >Paxos理論介紹(4): 動態成員變更

Paxos理論介紹(4): 動態成員變更

整個理論介紹中,只有這一篇最後的‘延時變更生效’整的我雲山霧罩的,沒太看懂,之後繼續研究。

多數派的本質

在講解成員變更之前,我們先回顧一下前文介紹的Paxos理論第一篇文章。文中提到Bqrm為一輪成功投票所需要的投票者集合,而Paxos演算法理論第二條約束要求任意兩個Bqrm的交集不為空,於是乎我們可以理解為Bqrm就是一個多數派的意思,因為在一個固定的投票者集合裡面,取多數派作為Bqrm,肯定是滿足條件的。

而所有的理論介紹,都是基於投票者集合是固定的。一旦投票者集合出現變化,Bqrm的定義將不再是多數派,Bqrm的取值將變得異常困難,而無法定義Bqrm,Paxos演算法的約束就無法達成一致性。也就是說,固定的成員是Paxos演算法的根基。

人肉配置進行成員變更?

我們再進行第二篇文章 Paxos理論介紹(2): Multi-Paxos與Leader 的回顧,通過文章我們知道Paxos是以獨立的例項的方式推進,從而產生一個一致的有序的系列,而每個例項都是單獨運作的Paxos演算法。再根據上文,我們得出一個要求,在相同的例項上,我們要求各個成員所認為的成員集合必須是一致的,也就是在一次完整的Paxos演算法裡面,成員其實還是固定的。

每個成員如何得知這個成員集合是什麼?通常我們是通過配置檔案。在通過配置的變更能否滿足以上的要求呢?我們知道Multi-Paxos在推進的過程中是允許少數派落後的,而在同一個例項裡面,獲知Value被chosen也是有先後的,那麼配置的變更可能出現在以上任何的先後夾縫內,下圖演示一個更換節點C為D的樣例。

這裡寫圖片描述

注:綠色代表已經獲知chosen value的例項

可以觀察到4這個例項,已經出現了成員混亂,(A,C),(B,D)都可以被認為是Bqrm,但明顯這兩個Bqrm沒有交集,已經違反Paxos協議。

事實上我們追求的是找到一個切入時機,使得Paxos的運作程式都在這相同的時刻完成配置的原子切換,但明顯在分散式環境裡面能做原子切換的只有一致性演算法,所以配置更新不靠譜。

題外話,如果真的要使用人肉配置更新,在工程上是有一些辦法,通過一些工具加人肉的細微觀察來無限逼近這個正確性,但終究只能逼近。在理論層面我們會放大任何現實中可能不會出現的細微錯誤,比如時間的不同步,網路包在交換機無限停留,作業系統排程導致的程式碼段卡殼等等,這些都會導致這些人肉方法不能上升到理論層面。況且,我們接下來要介紹的動態成員變更演算法也是非常的簡單。所以這些細緻的問題就不展開來聊了。

Paxos動態成員變更演算法

這個演算法在 Paxos Made Simple 的最後一段被一句話帶過,可能作者認為這個是水到渠成的事情,根本不值一提。

Multi-Paxos決議出的有序系列,一般被用來作為狀態機的狀態轉移輸入,一致的狀態轉移得出一致的狀態,這是Paxos的基本應用。那麼非常水到渠成的事情就是,成員(投票者集合)本身也是一個狀態,我們通過Paxos來決議出成員變更的操作系列,那麼各臺機器就能獲得一致的成員狀態。如下圖。

這裡寫圖片描述

在4這個例項,我們通過Paxos演算法來決議一個成員變更操作,所有的節點在例項4之後都能獲取到成員從A,B,C變成了A,B,D,在理論上達到了原子變更的要求。

延緩變更生效

通常Paxos的工程化為了效能和進展性都會由一個Leader節點進行資料寫入,而成員變更往往可能會導致Leader節點發生變化。那麼變化的瞬間可能會出現大量當前Leader節點堆積請求的失敗。

另外如果採用多個例項基於視窗滑動並行提交(什麼是視窗滑動並行提交?這裡暫時不展開講,而且我覺得這個在實際工程實現中必要性也不是很大)的話,在成員變更操作被chosen後,之後的例項可能還在Paxos演算法運行當中,那麼這些例項的Bqrm可能已經被確定下來,所以我們不能再去改動這些例項的Bqrm。如上圖例子,4的時候進行了成員變更,但是由於並行提交的關係,5和6可能都已經在提交當中了,那麼他的Bqrm還是被確定下來為A,B,C,這時候我們不能去改這些例項的Bqrm為A,B,D。

為了解決這個問題,我們可以將成員生效時間延緩一下,在這個期間將請求引導到新的寫入節點。

這裡寫圖片描述

我們可以定義一個延緩視窗為a,成員變更點為I,則生效點為I + a。這個a根據實際情況進行調節。如上圖,成員變更點在例項4,但是生效點可以在例項6之後。我們規定舊的Leader在I + a之前仍然能正常寫入資料,而新的寫入節點必須從I + a開始寫入資料,這樣可以完成一個平滑過渡。

這裡有個異常情況,如果成員變更後,舊的Leader不寫入資料了,例項停留在(I, I + a), 而新的寫入節點因為要在I + a後才能寫入資料,真個演算法就卡住無法進行請求寫入了。這種情況需要在工程上進行觀察,如果出現則由新的寫入節點對(I, I + a)進行nullvalue的寫入,從而填補空洞。