paxos算法理解 頂 原

主要從以下幾點理解paxos:

  1. 基礎paxos算法
    1. 階段一:prepare
    2. 階段二:accept
  2. multi-paxos
    1. 選擇log entry
    2. leader選舉
    3. 消除prepare RPC調用
    4. 全備份
  3. 客戶端協議
  4. 改變系統配置

基礎paxos

只有一個acceptor可以嗎? 不可以。所有的proposer都往一個acceptor上發消息,這個acceptor crashed,那麼整個系統就crashed。 解決方法:使用quorum (仲裁人數)。多個acceptor, 只需要大多數acceptor選中某個value就可以了,萬一某一個acceptor crashed,不影響系統。

每個acceptor 只chose第一個value,這樣可以嗎? 不可以。因爲這樣可能會出現proposal1的acceptor和proposal2的acceptor的數量一樣多,無法選出哪一個proposal作爲chosenproposal。例如server1 選中proposal1,server2 選中proposal1和proposal2,server3 選中proposal2。 解決方案:每個proposer在發起proposal前必須確認當前是否有proposal選中,如果有,則放棄自己的proposal。

chosen過程中會出現衝突,依據衝突不同得出結論: 一旦選中一個proposal,其他競選proposal必須選擇同樣的value;proposal按照proposal number排序,拒絕舊proposal;

通過上述描述, 可設計proposal number 結構如下: 由兩部分組成:

  1. round number: paxos執行的回數,每個服務器都必須保持最新的maxRound【保證儘量少的出現衝突,都競爭最大編號】
  2. server id:每個服務器有唯一的id【保證proposal編號不會相同 】

maxRound必須保存在永久存儲介質上,一段server crash,可以重新使用 round number 發起proposal

  • paxos各階段目標:
    • prepare階段
      1. 發現任任何被選擇的proposal。發現後將自己的value改爲發現的Value
      2. 阻塞還未完成的proposal。當proposal number較大的proposal進入prepare階段後,舊的proposal在accept階段會被拒絕。
    • accept階段
      1. 使得所有acceptor接受proposal。

主要邏輯:

|proposer|acceptor| |-|-| |1.選擇proposal number n|| |2.廣播給acceptor prepare(n)|| ||3. 響應prepare(n): if (n > minProposol) then minProposol=n; Return(acceptedProposol,acceptedValue)| |4. 獲取大多數acceptor回覆:如果有返回,選擇返回中最大的proposal number 對應的value作爲本次proposal的value;如果沒有返回,可繼續使用之前的value|| |5.向所有acceptor廣播accept(n,value)|| ||6. 響應accept(n,value):if(n >= minProposol) then acceptedProposol = minProposol = n; accptedValue = value; Return minProposol| |7. 獲取大多數acceptor返回:如果存在result>n;表示proposal被拒絕,重複1~6,且下次設置的n比result大,否則chosen proposal||

所有競爭的proposal必須有一個公共的acceptor,這樣就能發現新舊proposal,並及時終止舊proposal。

paxos活鎖:導致無proposal chosen。 proposal A 早prepare階段通過,另一proposal B進入prepare, acceptor的minProposal增加,導致A在accept階段被拒絕,A立即重試,並進入prepare階段,導致acceptor的minProposal增加,B進入accept階段後被拒絕。。。如此往復。沒有command能正常進行。

解決方案:每次重試必須在隨機的時延後進行。

multi-paxos

如何選擇log entry?

實現步驟:

  1. 找到第一個log entry 不知道是否chosen的entry slot,(不一定是第一個爲空的slot)
  2. 運行basic paxos, value爲client command,
  3. prepare return 中是否包含acceptedvalue?
    • 有:chosen acceptedvalue,並重復步驟1
    • 無:chosen client請求,該slot即是尋找的slot

例子:

| |1|2|3|4|5|6|7| |-|-|-|-|-|-|-|-| |server 1|mov|add|cmp|||ret|| |server 2|mov|add||sub||ret|| |server 3|mov|add|cmp||cmp|ret||

如上表,當client請求jmp時,假設server3 crashed,此時到slot3,由於server1和server2不同,不知道是否chosen Proposal,於是以client command的值爲value; 運行paxos,進入prepare(n),server1 slot3已經有acceptedvalue,所以Proposal的value會改爲server 1 slot 3的值,然後進行accept階段,server2 slot3 接收value。將server2 slot3 改爲cmp。

| |1|2|3|4|5|6|7| |-|-|-|-|-|-|-|-| |server 1|mov|add|cmp|||ret|| |server 2|mov|add|cmp|sub||ret|| |server 3|mov|add|cmp||cmp|ret||

同理,server1 slot4 改爲sub:

| |1|2|3|4|5|6|7| |-|-|-|-|-|-|-|-| |server 1|mov|add|cmp|sub||ret|| |server 2|mov|add|cmp|sub||ret|| |server 3|mov|add|cmp||cmp|ret||

然後slot5, acceptedValue=null;次次Proposal的value爲元定義的value,即client的command。

| |1|2|3|4|5|6|7| |-|-|-|-|-|-|-|-| |server 1|mov|add|cmp|sub|jmp|ret|| |server 2|mov|add|cmp|sub|jmp|ret|| |server 3|mov|add|cmp||cmp|ret|| 找到 log entry slot。

注意:上述server3 crashed!

在上述處理過程中:server可以同時接收多個client請求,只要能保證client 請求的log Entry不同即可; 但是在command進入server狀態機執行的時候,必須是順序進行。

如何改善multi-paxos性能?

multi-paxos的問題:

  1. 多個proposer進行時,會出現衝突和重試,降低系統chosen效率;
  2. 對每個選定的value需要進行2回RPC調用;

解決方案:

  1. 選擇learder。一次只有一個leader,這樣就不會有多proposer發生衝突

  2. 清除絕大多數的prepare RPC調用

    • 進行一次prepare
    • 大多數的log entry 能在一個RPC調用完成
  3. 如何選擇leader? 1.讓serverid最大的作爲leader; 2.每隔Tms,每個server發送心跳包給其他server, 3.如果server在2Tms內沒有收到比它大的server心跳,那麼它可以作爲leader,接受client 請求,擁有proposer角色 4.不是leader的server,拒絕client請求將請求重新定向到leader,acceptor角色。

  4. 怎麼處理prepare階段

    1. 爲什麼需要prepare階段?
      1. 阻塞舊Proposal:
        • 將Proposal與logEntry slot關聯,每個Proposal number表示一個slot
      2. 查找可能chosen的value:
        • 最大的acceptedProposal 的values;
        • 當前log entry slot中,每個server的當前slot都沒有acceptedvalue,返回noMoreAccepted
    2. 改進後:
      1. 如果acceptor返回noMoreAccepted,則跳過同slot當前server 後的所有的prepare RPC(直到accept拒絕Proposal,拒絕Proposal說明acceptro層接受過Proposal,存在acceptedvalue)。
      2. 如果leader得知noMoreAccepted,不需要使用prepare了,直接進入accpt階段。因爲所有server的該slot均爲空,直接通知acceptor accept Proposal。此時只需要一輪accept RPC。

怎麼全備份? 目標:每個server上的備份都是全備份。

目標細分:

  1. log entry沒有full replication:full replication
  2. 只有proposer知道那個entry被chosen:所有server都知道那個entry被選中。

解決步驟:

  1. proposer在後臺一直accept 請求RPC,直到到所有server都回應。能保證大多數server都是full replication(爲什麼只是大多數?因爲有可能之前有server crash,有些log entry爲空,就算迴應一次,並不能把所有slot都都填充完整,操作布恩那個進入狀態機執行,不能達到full replication)
  2. track chosen entries
    1. 使得entries能被知道是否已經chosen:acceptedEntry[i] = 無窮大 表示第i個entry被chosen。
    2. 每個server都維護一個firstUnchosenIndex,該索引是entry的索引,表示該索引前的entry均被chosen。
  3. proposer(也就是leader)告知acceptor選擇entry:
    1. proposer在accept RPC時,發送firstUnchosenIndex。那麼acceptor知道entry索引小於firstUnchosenIndex的均被chosen。
    2. acceptor標記同時滿足以下條件的entry爲chosen:i<firstUnchosenIndex && accptedProposal[i] == proposal【proposal相等即表示是同一個leader發送的proposal,又index < firstUnchosenIndex,可知前面均chosen,】
  4. acceptor不能確認proposal number不同的log entry 是否chosen。 解決方案:acceptor在返回時,返回acceptor的firstUnchosenIndex,若proposer的firstUnchosenIndex 大於acceptor的firstUnchosenIndex, 那麼proposer在後臺發送[success] RPC。
success(Index, v):
acceptedValue[index] = v;
acceptedProposal[index]=無窮大
return firstUnchosenIndex

客戶端協議

  1. 發送command給leader
    1. 如果leader down, 發送消息給任意的server
    2. 如果聯繫的server不是leader,自動重定向到leader
  2. leader在command被log entry選中後,在leader的狀態機中執行,執行出結果,然後迴應client
  3. 請求超時
    1. clinet請求別的server
    2. 重定向leader server
    3. 重新請求command
  4. 如果leader在執行後,響應client前crash,一條command不能被執行兩次
    1. client爲每個command提供唯一的標識
    2. server 在log entry中保存command id
    3. 狀態機保最近的每個client的commandid
    4. 執行command前,狀態機檢查command是否已經執行過,執行過,直接忽略並返回old command的執行結果。

如果client在發送command後,接受響應前crash, 然後重新登陸,會遇到同樣的問題。client會提交兩次command,使用上述措施可以保證每條command只執行一次。

配置修改

系統配置:serverid和address 這些直接會改變quorum。造成系統的majority數量的改變。

爲什麼需要系統設置變化:

  1. server crash
  2. replication change

安全原則:在配置變動時,避免對同一個log entry 有不同數量的majority和不同的value選擇。

解決方案:使用paxos中log entry管理配置變動

  1. 配置保存爲log entry
  2. 和其他log entry一樣備份
  3. 在choseing log entry i 時 使用log entry index爲i-a作爲系統配置。(其中a爲系統參數,在系統啓動時定義)

引入a後:

  1. 系統的併發數量必須在a以內:因爲在log entry i 被chosen後才能 chosen entry a+i; 可以使用一些無操作的command加速配置改變。

核心爲代碼

核心邏輯僞代碼:

--- Paxos Proposer ---
proposer(v):
	while not decided:
	choose n, unique and higher than any n seen so far
	send prepare(n) to all servers including self
	if prepare_ok(n, na, va) from majority:
		v’ = va with highest na; choose own v otherwise
		send accept(n, v’) to all
		if accept_ok(n) from majority:
			send decided(v’) to all
--- Paxos Acceptor ---
acceptor state on each node (persistent):
np --- highest prepare seen
na, va --- highest accept seen

acceptor’s prepare(n) handler:
	if n > np
		np = n
		reply prepare_ok(n, na, va)
	else
		reply prepare_reject

acceptor’s accept(n, v) handler:
	if n >= np
	np = n
	na = n
	va = v
	reply accept_ok(n)
	else
		reply accept_reject

readmore:算法證明

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章