关于Paxos 幽灵复现问题的看法
由于郁白之前寫的關(guān)于Multi-Paxos 的文章流傳非常廣, 原文提出了一個叫"幽靈復(fù)現(xiàn)" 的問題, 認為這個是一個很詭異的問題, 后續(xù)和很多人交流關(guān)于一致性協(xié)議的時候, 也經(jīng)常會提起這個問題, 但是其實這個問題我認為就是常見的"第三態(tài)"問題加了一層包裝而已.
幽靈復(fù)現(xiàn)問題
來自郁白的博客:
使用Paxos協(xié)議處理日志的備份與恢復(fù),可以保證確認形成多數(shù)派的日志不丟失,但是無法避免一種被稱為“幽靈復(fù)現(xiàn)”的現(xiàn)象,如下圖所示:
| 第一輪 | A | 1-10 | 1-5 | 1-5 |
| 第二輪 | B | 宕機 | 1-6,20 | 1-6,20 |
| 第三輪 | A | 1-20 | 1-20 | 1-20 |
對于將Paxos協(xié)議應(yīng)用在數(shù)據(jù)庫日志同步場景的情況,幽靈復(fù)現(xiàn)問題是不可接受,一個簡單的例子就是轉(zhuǎn)賬場景,用戶轉(zhuǎn)賬時如果返回結(jié)果超時,那么往往會查詢一下轉(zhuǎn)賬是否成功,來決定是否重試一下。如果第一次查詢轉(zhuǎn)賬結(jié)果時,發(fā)現(xiàn)未生效而重試,而轉(zhuǎn)賬事務(wù)日志作為幽靈復(fù)現(xiàn)日志重新出現(xiàn)的話,就造成了用戶重復(fù)轉(zhuǎn)賬。
為了處理“幽靈復(fù)現(xiàn)”問題,我們在每條日志的內(nèi)容中保存一個generateID,leader在生成這條日志時以當前的leader ProposalID作為generateID。按logID順序回放日志時,因為leader在開始服務(wù)之前一定會寫一條StartWorking日志,所以如果出現(xiàn)generateID相對前一條日志變小的情況,說明這是一條“幽靈復(fù)現(xiàn)”日志(它的generateID會小于StartWorking日志),要忽略掉這條日志。
第三態(tài)問題
第三態(tài)問題也是我們之前經(jīng)常講的問題, 其實在網(wǎng)絡(luò)系統(tǒng)里面, 對于一個請求都有三種返回結(jié)果
前面兩種狀態(tài)由于服務(wù)端都有明確的返回結(jié)果, 所以非常好處理, 但是如果是第三種狀態(tài)的返回, 由于是超時狀態(tài), 所以服務(wù)端可能對于這個命令是請求是執(zhí)行成功, 也有可能是執(zhí)行失敗的, 所以如果這個請求是一個寫入操作, 那么下一次的讀取請求可能讀到這個結(jié)果, 也可能讀到的結(jié)果是空的
就像在 raft phd 那個論文里面說的, 這個問題其實是和 raft/multi-paxos 協(xié)議無關(guān)的內(nèi)容, 只要在分布式系統(tǒng)里面都會存在這個問題, 所以大部分的解決方法是兩個
那么對應(yīng)于raft 中的第三態(tài)問題是, 當最后log Index 為4 的請求超時的時候, 狀態(tài)機中出現(xiàn)的兩種場景都是可能的
所以下一次讀取的時候有可能讀到log Index 4 的內(nèi)容, 也有可能讀不到, 所以如果在發(fā)生了超時請求以后, 默認client 需要進行重試直到這個操作成功以后, 接下來才可以保證讀到的寫入結(jié)果. 這也是工程實現(xiàn)里面常見的做法
對應(yīng)于幽靈問題, 其實是由于6-10 的操作產(chǎn)生了超時操作, 由于產(chǎn)生了超時操作以后, client 并沒有對這些操作進行確認, 而是接下來去讀取這個結(jié)果, 那么讀取不到這個里面的內(nèi)容, 由于后續(xù)的寫入和切主操作有重新能夠讀取到這個6-10 的內(nèi)容了, 造成了幽靈復(fù)現(xiàn), 導(dǎo)致這個問題的原因還是因為沒有進行對超時操作的重確認.
回到幽靈復(fù)現(xiàn)問題
那么Raft 有沒有可能出現(xiàn)這個幽靈復(fù)現(xiàn)問題呢?
其實在早期Raft 沒有引入新的Leader 需要寫入一個包含自己的空的Entry 的時候也一樣會出現(xiàn)這個問題
Log Index 4,5 客戶端超時未給用戶返回, 存在以下日志場景
然后 (a) 節(jié)點宕機, 這個時候client 是查詢不到 Log entry 4, 5 里面的內(nèi)容
在(b)或(c) 成為Leader 期間, 沒有寫入任何內(nèi)容, 然后(a) 又恢復(fù), 并且又重新選主, 那么就存在一下日志, 這個時候client 再查詢就查詢到Log entry 4,5 里面的內(nèi)容了
那么Raft 里面加入了新Leader 必須寫入一條當前Term 的Log Entry 就可以解決這個問題, 其實和之前郁白提到的寫入一個StartWorking 日志是一樣的做法, 由于(b), (c) 有一個Term 3的日志, 就算(a) 節(jié)點恢復(fù)過來, 也無法成了Leader, 那么后續(xù)的讀也就不會讀到Log Entry 4, 5 里面的內(nèi)容
那么這個問題的本質(zhì)是什么呢?
其實這個問題的本質(zhì)是對于一致性協(xié)議在recovery 的不同做法產(chǎn)生的.?
也就是說對于一個在多副本里面未達成一致的Log entry, 在Recovery 需要如何處理這一部分未達成一致的log entry.
對于這一部分log entry 其實可以是提交, 也可以是不提交, 因為會產(chǎn)生這樣的log entry, 一定是之前對于這個client 的請求超時返回了.
常見的Multi-Paxos 在對這一部分日志進行重確認的時候, 默認是將這部分的內(nèi)容提交的, 也就是通過重確認的過程默認去提交這些內(nèi)容
而Raft 的實現(xiàn)是默認對這部分的內(nèi)容是不提交的, 也就是增加了一個當前Term 的空的Entry, 來把之前l(fā)eader 多余的log 默認不提交了, 幽靈復(fù)現(xiàn)里面其實也是通過增加一個空的當前Leader 的Proposal ID 來把之前的Log Entry 默認不提交
所以這個問題只是對于返回超時, 未達成一致的Log entry 的不同的處理方法造成的.
在默認去提交這些日志的場景, 在寫入超時以后讀取不到內(nèi)容, 但是通過recovery 以后又能夠讀取到這個內(nèi)容, 就產(chǎn)生了幽靈復(fù)現(xiàn)的問題
但是其實之所以會出現(xiàn)幽靈復(fù)現(xiàn)的問題是因為在有了一個超時的第三態(tài)的請求以后, 在沒有處理好這個第三態(tài)請求之前, 出現(xiàn)成功和失敗都是有可能的.
所以本質(zhì)是在Multi-Paxos 實現(xiàn)中, 在recovery 階段, 將未達成一致的Log entry 提交造成的幽靈復(fù)現(xiàn)的問題, 本質(zhì)是沒有處理好這個第三態(tài)的請求.
?
一站式開發(fā)者服務(wù),海量學(xué)習(xí)資源0元起!
阿里熱門開源項目、機器學(xué)習(xí)干貨、開發(fā)者課程/工具、小微項目、移動研發(fā)等海量資源;更有開發(fā)者福利Kindle、技術(shù)圖書幸運抽獎,100%中--》https://www.aliyun.com/acts/product-section-2019/developer?utm_content=g_1000047140
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的关于Paxos 幽灵复现问题的看法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云栖专辑 | 阿里开发者们的第3个感悟:
- 下一篇: 码上用它开始Flutter混合开发——F