因爲鬱白以前寫的關於Multi-Paxos 的文章流傳很是廣, 具體地址: http://oceanbase.org.cn/?p=111原文提出了一個叫"幽靈復現" 的問題, 認爲這個是一個很詭異的問題, 後續和不少人交流關於一致性協議的時候, 也常常會提起這個問題, 可是其實這個問題我認爲就是常見的"第三態"問題加了一層包裝而已.git
幽靈復現問題github
來自鬱白的博客:數據庫
使用Paxos協議處理日誌的備份與恢復,能夠保證確認造成多數派的日誌不丟失,可是沒法避免一種被稱爲「幽靈復現」的現象,以下圖所示:網絡
Leader | A | B | C | |
---|---|---|---|---|
第一輪 | A | 1-10 | 1-5 | 1-5 |
第二輪 | B | 宕機 | 1-6,20 | 1-6,20 |
第三輪 | A | 1-20 | 1-20 | 1-20 |
對於將Paxos協議應用在數據庫日誌同步場景的狀況,幽靈復現問題是不可接受,一個簡單的例子就是轉帳場景,用戶轉帳時若是返回結果超時,那麼每每會查詢一下轉帳是否成功,來決定是否重試一下。若是第一次查詢轉帳結果時,發現未生效而重試,而轉帳事務日誌做爲幽靈復現日誌從新出現的話,就形成了用戶重複轉帳。機器學習
爲了處理「幽靈復現」問題,咱們在每條日誌的內容中保存一個generateID,leader在生成這條日誌時以當前的leader ProposalID做爲generateID。按logID順序回放日誌時,由於leader在開始服務以前必定會寫一條StartWorking日誌,因此若是出現generateID相對前一條日誌變小的狀況,說明這是一條「幽靈復現」日誌(它的generateID會小於StartWorking日誌),要忽略掉這條日誌。分佈式
第三態問題工具
第三態問題也是咱們以前常常講的問題, 其實在網絡系統裏面, 對於一個請求都有三種返回結果學習
前面兩種狀態因爲服務端都有明確的返回結果, 因此很是好處理, 可是若是是第三種狀態的返回, 因爲是超時狀態, 因此服務端可能對於這個命令是請求是執行成功, 也有多是執行失敗的, 因此若是這個請求是一個寫入操做, 那麼下一次的讀取請求可能讀到這個結果, 也可能讀到的結果是空的優化
就像在 raft phd 那個論文裏面說的, 這個問題實際上是和 raft/multi-paxos 協議無關的內容, 只要在分佈式系統裏面都會存在這個問題, 因此大部分的解決方法是兩個3d
那麼對應於raft 中的第三態問題是, 當最後log Index 爲4 的請求超時的時候, 狀態機中出現的兩種場景都是可能的
因此下一次讀取的時候有可能讀到log Index 4 的內容, 也有可能讀不到, 因此若是在發生了超時請求之後, 默認client 須要進行重試直到這個操做成功之後, 接下來才能夠保證讀到的寫入結果. 這也是工程實現裏面常見的作法
對應於幽靈問題, 實際上是因爲6-10 的操做產生了超時操做, 因爲產生了超時操做之後, client 並無對這些操做進行確認, 而是接下來去讀取這個結果, 那麼讀取不到這個裏面的內容, 因爲後續的寫入和切主操做有從新可以讀取到這個6-10 的內容了, 形成了幽靈復現, 致使這個問題的緣由仍是由於沒有進行對超時操做的重確認.
回到幽靈復現問題
那麼Raft 有沒有可能出現這個幽靈復現問題呢?
其實在早期Raft 沒有引入新的Leader 須要寫入一個包含本身的空的Entry 的時候也同樣會出現這個問題
Log Index 4,5 客戶端超時未給用戶返回, 存在如下日誌場景
而後 (a) 節點宕機, 這個時候client 是查詢不到 Log entry 4, 5 裏面的內容
在(b)或(c) 成爲Leader 期間, 沒有寫入任何內容, 而後(a) 又恢復, 而且又從新選主, 那麼就存在一下日誌, 這個時候client 再查詢就查詢到Log entry 4,5 裏面的內容了
那麼Raft 裏面加入了新Leader 必須寫入一條當前Term 的Log Entry 就能夠解決這個問題, 其實和以前鬱白提到的寫入一個StartWorking 日誌是同樣的作法, 因爲(b), (c) 有一個Term 3的日誌, 就算(a) 節點恢復過來, 也沒法成了Leader, 那麼後續的讀也就不會讀到Log Entry 4, 5 裏面的內容
那麼這個問題的本質是什麼呢?
其實這個問題的本質是對於一致性協議在recovery 的不一樣作法產生的. 關於一致性協議在不一樣階段的作法能夠看這個文章 http://baotiao.github.io/2018/01/02/consensus-recovery/
也就是說對於一個在多副本里面未達成一致的Log entry, 在Recovery 須要如何處理這一部分未達成一致的log entry.
對於這一部分log entry 其實能夠是提交, 也能夠是不提交, 由於會產生這樣的log entry, 必定是以前對於這個client 的請求超時返回了.
常見的Multi-Paxos 在對這一部分日誌進行重確認的時候, 默認是將這部分的內容提交的, 也就是經過重確認的過程默認去提交這些內容
而Raft 的實現是默認對這部分的內容是不提交的, 也就是增長了一個當前Term 的空的Entry, 來把以前leader 多餘的log 默認不提交了, 幽靈復現裏面其實也是經過增長一個空的當前Leader 的Proposal ID 來把以前的Log Entry 默認不提交
因此這個問題只是對於返回超時, 未達成一致的Log entry 的不一樣的處理方法形成的.
在默認去提交這些日誌的場景, 在寫入超時之後讀取不到內容, 可是經過recovery 之後又可以讀取到這個內容, 就產生了幽靈復現的問題
可是其實之因此會出現幽靈復現的問題是由於在有了一個超時的第三態的請求之後, 在沒有處理好這個第三態請求以前, 出現成功和失敗都是有可能的.
因此本質是在Multi-Paxos 實現中, 在recovery 階段, 將未達成一致的Log entry 提交形成的幽靈復現的問題, 本質是沒有處理好這個第三態的請求.
一站式開發者服務,海量學習資源0元起!
阿里熱門開源項目、機器學習乾貨、開發者課程/工具、小微項目、移動研發等海量資源;更有開發者福利Kindle、技術圖書幸運抽獎,100%中--》https://www.aliyun.com/acts/product-section-2019/developer?utm_content=g_1000047140