五分鐘學後端技術:分佈式系統理論 - 從放棄到入門

轉載聲明html

本系列文章轉自某技術大佬的博客https://www.cnblogs.com/bange...node

該系列文章是我在網上可以找到的最全面的分佈式理論介紹文章了,一直沒看到有人整理這個系列文章,因此此次我就來作技術好文的搬運工,給整合了一把,以爲寫得好的朋友不妨去這位大佬的博客上打賞一把。程序員

分佈式系統理論 - 從放棄到入門web

隨承載用戶數量的增長和容災的須要,愈來愈多互聯網後臺系統從單機模式切換到分佈式集羣。回顧本身畢業五年來的工做內容,一樣有這樣的轉變。算法

畢業頭兩年負責維護運行在刀片機上的業務,在機房裏拔插單板的日子是我逝去的青春。設備之間經過VCS組成冷備,但即便有雙機軟件保護,宕機、網絡丟包等問題發生時業務仍會受影響。這樣的系統架構下爲保證SLA,有時候須要深刻Linux系統內核或硬件層面分析機器重啓的緣由。數據庫

接下來負責維護承載在分佈式集羣上的業務,相比前面的工做,這個階段主要關注點不是單節點的異常,更可能是系統總體的穩定和健壯。面對紛繁複雜的系統,剛開始的時候有這樣的感受:promise

龐大複雜的分佈式系統前,應該從哪方面入手提高對其的認識和理解、提高專業性?網上能夠找到不少分佈式系統相關的論文和資料,但概括起來要表達的主要意思是什麼?緩存

結合本身這幾年的工做經驗,總結分佈式系統的核心就是解決一個問題:不一樣節點間如何達成共識。微信

看似簡單的問題因網絡丟包、節點宕機恢復等場景變得複雜,由此才衍生出不少概念、協議和理論。爲探究共識問題最大能解決的程度,因而有FLP、CAP邊界理論;爲在特定條件和範圍內解決該問題,因而有一致性協議Paxos、Raft、Zab和Viewstamped Replication;爲構建這些協議,因而有多數派、Leader選舉、租約、邏輯時鐘等概念和方法。網絡

2016年我閱讀了分佈式系統領域一些表明性的論文和博文,圍繞「不一樣節點如何達成共識」這個問題,加入本身的認識和理解後有下面7篇小結:

一致性、2PC和3PC
選舉、多數派和租約
時間、時鐘和事件順序
CAP
Paxos
Raft、Zab
Paxos變種和優化
構思和寫做技術類文章是一個辛苦的過程,一方面要閱讀不少資料並轉化成本身的理解、找到儘可能不拾人牙慧的立意和角度,一方面要絞盡腦汁組織語言讓預期的讀者可以容易理解。

但它也是一個有趣的過程,把知識捋一遍後本來一些模糊的概念變得清晰,寫做過程當中想到的一些有意思的內容我也會將它穿插到文章裏,有時候會被本身想到的一些小機靈逗樂 :)

但願這幾篇整理能爲系統性地介紹分佈式理論中文資料添一塊磚、加一片瓦。

分佈式系統理論基礎 - 一致性、2PC和3PC

引言

狹義的分佈式系統指由網絡鏈接的計算機系統,每一個節點獨立地承擔計算或存儲任務,節點間經過網絡協同工做。廣義的分佈式系統是一個相對的概念,正如Leslie Lamport所說[1]:

What is a distributed systeme. Distribution is in the eye of the beholder. To the user sitting at the keyboard, his IBM personal computer is a nondistributed system. To a flea crawling around on the circuit board, or to the engineer who designed it, it's very much a distributed system.
一致性是分佈式理論中的根本性問題,近半個世紀以來,科學家們圍繞着一致性問題提出了不少理論模型,依據這些理論模型,業界也出現了不少工程實踐投影。下面咱們從一致性問題、特定條件下解決一致性問題的兩種方法(2PC、3PC)入門,瞭解最基礎的分佈式系統理論。

一致性(consensus)

何爲一致性問題?簡單而言,一致性問題就是相互獨立的節點之間如何達成一項決議的問題。分佈式系統中,進行數據庫事務提交(commit transaction)、Leader選舉、序列號生成等都會遇到一致性問題。這個問題在咱們的平常生活中也很常見,好比牌友怎麼商定幾點在哪打幾圈麻將:

《賭聖》,1990

假設一個具備N個節點的分佈式系統,當其知足如下條件時,咱們說這個系統知足一致性:

全認同(agreement): 全部N個節點都認同一個結果
值合法(validity): 該結果必須由N個節點中的節點提出
可結束(termination): 決議過程在必定時間內結束,不會無休止地進行下去
有人可能會說,決定何時在哪搓搓麻將,4我的商量一下就ok,這不很簡單嗎?

但就這樣看似簡單的事情,分佈式系統實現起來並不輕鬆,由於它面臨着這些問題:

消息傳遞異步無序(asynchronous): 現實網絡不是一個可靠的信道,存在消息延時、丟失,節點間消息傳遞作不到同步有序(synchronous)
節點宕機(fail-stop): 節點持續宕機,不會恢復
節點宕機恢復(fail-recover): 節點宕機一段時間後恢復,在分佈式系統中最多見
網絡分化(network partition): 網絡鏈路出現問題,將N個節點隔離成多個部分
拜占庭將軍問題(byzantine failure)[2]: 節點或宕機或邏輯失敗,甚至不按套路出牌拋出干擾決議的信息
假設現實場景中也存在這樣的問題,咱們看看結果會怎樣:

複製代碼; "複製代碼")

我: 老王,今晚7點老地方,搓夠48圈不見不散!
……
(次日凌晨3點) 隔壁老王: 沒問題! // 消息延遲

我: ……

我: 小張,今晚7點老地方,搓夠48圈不見不散!
小張: No ……
(兩小時後……)
小張: No problem! // 宕機節點恢復

我: ……

我: 老李頭,今晚7點老地方,搓夠48圈不見不散!
老李: 必須的,大保健走起! // 拜占庭將軍 (這是要打麻將呢?仍是要大保健?仍是一邊打麻將一邊大保健……)
還能不能一塊兒愉快地玩耍...

咱們把以上所列的問題稱爲系統模型(system model),討論分佈式系統理論和工程實踐的時候,必先劃定模型。例若有如下兩種模型:

異步環境(asynchronous)下,節點宕機(fail-stop)
異步環境(asynchronous)下,節點宕機恢復(fail-recover)、網絡分化(network partition)
2比1多了節點恢復、網絡分化的考量,於是對這兩種模型的理論研究和工程解決方案一定是不一樣的,在尚未明晰所要解決的問題前談解決方案都是一本正經地耍流氓。

一致性還具有兩個屬性,一個是強一致(safety),它要求全部節點狀態一致、共進退;一個是可用(liveness),它要求分佈式系統24*7無間斷對外服務。FLP定理(FLP impossibility)3 已經證實在一個收窄的模型中(異步環境並只存在節點宕機),不能同時知足 safety 和 liveness。

FLP定理是分佈式系統理論中的基礎理論,正如物理學中的能量守恆定律完全否認了永動機的存在,FLP定理否認了同時知足safety 和 liveness 的一致性協議的存在。

《怦然心動 (Flipped)》,2010

工程實踐上根據具體的業務場景,或保證強一致(safety),或在節點宕機、網絡分化的時候保證可用(liveness)。2PC、3PC是相對簡單的解決一致性問題的協議,下面咱們就來了解2PC和3PC。

2PC

2PC(tow phase commit)兩階段提交[5]顧名思義它分紅兩個階段,先由一方進行提議(propose)並收集其餘節點的反饋(vote),再根據反饋決定提交(commit)或停止(abort)事務。咱們將提議的節點稱爲協調者(coordinator),其餘參與決議節點稱爲參與者(participants, 或cohorts):

2PC, phase one

在階段1中,coordinator發起一個提議,分別問詢各participant是否接受。

2PC, phase two

在階段2中,coordinator根據participant的反饋,提交或停止事務,若是participant所有贊成則提交,只要有一個participant不一樣意就停止。

在異步環境(asynchronous)而且沒有節點宕機(fail-stop)的模型下,2PC能夠知足全認同、值合法、可結束,是解決一致性問題的一種協議。但若是再加上節點宕機(fail-recover)的考慮,2PC是否還能解決一致性問題呢?

coordinator若是在發起提議後宕機,那麼participant將進入阻塞(block)狀態、一直等待coordinator迴應以完成該次決議。這時須要另外一角色把系統從不可結束的狀態中帶出來,咱們把新增的這一角色叫協調者備份(coordinator watchdog)。coordinator宕機必定時間後,watchdog接替原coordinator工做,經過問詢(query) 各participant的狀態,決定階段2是提交仍是停止。這也要求 coordinator/participant 記錄(logging)歷史狀態,以備coordinator宕機後watchdog對participant查詢、coordinator宕機恢復後從新找回狀態。

從coordinator接收到一次事務請求、發起提議到事務完成,通過2PC協議後增長了2次RTT(propose+commit),帶來的時延(latency)增長相對較少。

3PC

3PC(three phase commit)即三階段提交6,既然2PC能夠在異步網絡+節點宕機恢復的模型下實現一致性,那還須要3PC作什麼,3PC是什麼鬼?

在2PC中一個participant的狀態只有它本身和coordinator知曉,假如coordinator提議後自身宕機,在watchdog啓用前一個participant又宕機,其餘participant就會進入既不能回滾、又不能強制commit的阻塞狀態,直到participant宕機恢復。這引出兩個疑問:

能不能去掉阻塞,使系統能夠在commit/abort前回滾(rollback)到決議發起前的初始狀態
當次決議中,participant間能不能相互知道對方的狀態,又或者participant間根本不依賴對方的狀態
相比2PC,3PC增長了一個準備提交(prepare to commit)階段來解決以上問題:

圖片截取自wikipedia

coordinator接收完participant的反饋(vote)以後,進入階段2,給各個participant發送準備提交(prepare to commit)指令。participant接到準備提交指令後能夠鎖資源,但要求相關操做必須可回滾。coordinator接收完確認(ACK)後進入階段三、進行commit/abort,3PC的階段3與2PC的階段2無異。協調者備份(coordinator watchdog)、狀態記錄(logging)一樣應用在3PC。

participant若是在不一樣階段宕機,咱們來看看3PC如何應對:

階段1: coordinator或watchdog未收到宕機participant的vote,直接停止事務;宕機的participant恢復後,讀取logging發現未發出同意vote,自行停止該次事務
階段2: coordinator未收到宕機participant的precommit ACK,但由於以前已經收到了宕機participant的同意反饋(否則也不會進入到階段2),coordinator進行commit;watchdog能夠經過問詢其餘participant得到這些信息,過程同理;宕機的participant恢復後發現收到precommit或已經發出同意vote,則自行commit該次事務
階段3: 即使coordinator或watchdog未收到宕機participant的commit ACK,也結束該次事務;宕機的participant恢復後發現收到commit或者precommit,也將自行commit該次事務
由於有了準備提交(prepare to commit)階段,3PC的事務處理延時也增長了1個RTT,變爲3個RTT(propose+precommit+commit),可是它防止participant宕機後整個系統進入阻塞態,加強了系統的可用性,對一些現實業務場景是很是值得的。

小結

以上介紹了分佈式系統理論中的部分基礎知識,闡述了一致性(consensus)的定義和實現一致性所要面臨的問題,最後討論在異步網絡(asynchronous)、節點宕機恢復(fail-recover)模型下2PC、3PC怎麼解決一致性問題。

閱讀前人對分佈式系統的各項理論研究,其中有嚴謹地推理、證實,有一種數學的美;觀現實中的分佈式系統實現,是綜合各類因素下妥協的結果。

分佈式系統理論基礎 - 選舉、多數派和租約

選舉(election)是分佈式系統實踐中常見的問題,經過打破節點間的對等關係,選得的leader(或叫master、coordinator)有助於實現事務原子性、提高決議效率。 多數派(quorum)的思路幫助咱們在網絡分化的狀況下達成決議一致性,在leader選舉的場景下幫助咱們選出惟一leader。租約(lease)在必定期限內給予節點特定權利,也能夠用於實現leader選舉。

下面咱們就來學習分佈式系統理論中的選舉、多數派和租約。

選舉(electioin)

一致性問題(consistency)是獨立的節點間如何達成決議的問題,選出你們都承認的leader本質上也是一致性問題,於是如何應對宕機恢復、網絡分化等在leader選舉中也須要考量。

Bully算法[1]是最多見的選舉算法,其要求每一個節點對應一個序號,序號最高的節點爲leader。leader宕機後次高序號的節點被重選爲leader,過程以下:

(a). 節點4發現leader不可達,向序號比本身高的節點發起從新選舉,從新選舉消息中帶上本身的序號

(b)(c). 節點五、6接收到重選信息後進行序號比較,發現自身的序號更大,向節點4返回OK消息並各自向更高序號節點發起從新選舉

(d). 節點5收到節點6的OK消息,而節點6通過超時時間後收不到更高序號節點的OK消息,則認爲本身是leader

(e). 節點6把本身成爲leader的信息廣播到全部節點

回顧《分佈式系統理論基礎 - 一致性、2PC和3PC》就能夠看到,Bully算法中有2PC的身影,都具備提議(propose)和收集反饋(vote)的過程。

在一致性算法Paxos、ZAB[2]、Raft[3]中,爲提高決議效率均有節點充當leader的角色。ZAB、Raft中描述了具體的leader選舉實現,與Bully算法相似ZAB中使用zxid標識節點,具備最大zxid的節點表示其所具有的事務(transaction)最新、被選爲leader。

多數派(quorum)

在網絡分化的場景下以上Bully算法會遇到一個問題,被分隔的節點都認爲本身具備最大的序號、將產生多個leader,這時候就須要引入多數派(quorum)[4]。多數派的思路在分佈式系統中很常見,其確保網絡分化狀況下決議惟一。

多數派的原理提及來很簡單,假如節點總數爲2f+1,則一項決議獲得多於 f 節點同意則得到經過。leader選舉中,網絡分化場景下只有具有多數派節點的部分纔可能選出leader,這避免了多leader的產生。

多數派的思路還被應用於副本(replica)管理,根據業務實際讀寫比例調整寫副本數Vw、讀副本數Vr,用以在可靠性和性能方面取得平衡[5]。

租約(lease)

選舉中很重要的一個問題,以上還沒有提到:怎麼判斷leader不可用、何時應該發起從新選舉?最早可能想到會經過心跳(heart beat)判別leader狀態是否正常,但在網絡擁塞或瞬斷的狀況下,這容易致使出現雙主。

租約(lease)是解決該問題的經常使用方法,其最初提出時用於解決分佈式緩存一致性問題[6],後面在分佈式鎖[7]等不少方面都有應用。

租約的原理一樣不復雜,中心思想是每次租約時長內只有一個節點得到租約、到期後必須從新頒發租約。假設咱們有租約頒發節點Z,節點0、1和2競選leader,租約過程以下:

(a). 節點0、一、2在Z上註冊本身,Z根據必定的規則(例如先到先得)頒發租約給節點,該租約同時對應一個有效時長;這裏假設節點0得到租約、成爲leader

(b). leader宕機時,只有租約到期(timeout)後才從新發起選舉,這裏節點1得到租約、成爲leader

租約機制確保了一個時刻最多隻有一個leader,避免只使用心跳機制產生雙主的問題。在實踐應用中,zookeeper、ectd可用於租約頒發。

小結

在分佈式系統理論和實踐中,常見leader、quorum和lease的身影。分佈式系統內不必定事事協商、事事民主,leader的存在有助於提高決議效率。

本文以leader選舉做爲例子引入和講述quorum、lease,固然quorum和lease是兩種思想,並不限於leader選舉應用。

最後提一個有趣的問題與你們思考,leader選舉的本質是一致性問題,Paxos、Raft和ZAB等解決一致性問題的協議和算法自己又須要或依賴於leader,怎麼理解這個看似「蛋生雞、雞生蛋」的問題?[8]

分佈式系統理論基礎 - 時間、時鐘和事件順序

十六號…… 四月十六號。一九六零年四月十六號下午三點以前的一分鐘你和我在一塊兒,由於你我會記住這一分鐘。從如今開始咱們就是一分鐘的朋友,這是事實,你改變不了,由於已通過去了。我明天會再來。

—— 《阿飛正傳》
現實生活中時間是很重要的概念,時間能夠記錄事情發生的時刻、比較事情發生的前後順序。分佈式系統的一些場景也須要記錄和比較不一樣節點間事件發生的順序,但不一樣於平常生活使用物理時鐘記錄時間,分佈式系統使用邏輯時鐘記錄事件順序關係,下面咱們來看分佈式系統中幾種常見的邏輯時鐘。

物理時鐘 vs 邏輯時鐘

可能有人會問,爲何分佈式系統不使用物理時鐘(physical clock)記錄事件?每一個事件對應打上一個時間戳,當須要比較順序的時候比較相應時間戳就行了。

這是由於現實生活中物理時間有統一的標準,而分佈式系統中每一個節點記錄的時間並不同,即便設置了 NTP 時間同步節點間也存在毫秒級別的誤差1。於是分佈式系統須要有另外的方法記錄事件順序關係,這就是邏輯時鐘(logical clock)。

Lamport timestamps

Leslie Lamport 在1978年提出邏輯時鐘的概念,並描述了一種邏輯時鐘的表示方法,這個方法被稱爲Lamport時間戳(Lamport timestamps)[3]。

分佈式系統中按是否存在節點交互可分爲三類事件,一類發生於節點內部,二是發送事件,三是接收事件。Lamport時間戳原理以下:

圖1: Lamport timestamps space time (圖片來源: wikipedia)

每一個事件對應一個Lamport時間戳,初始值爲0
若是事件在節點內發生,時間戳加1
若是事件屬於發送事件,時間戳加1並在消息中帶上該時間戳
若是事件屬於接收事件,時間戳 = Max(本地時間戳,消息中的時間戳) + 1
假設有事件a、b,C(a)、C(b)分別表示事件a、b對應的Lamport時間戳,若是C(a) < C(b),則有a發生在b以前(happened before),記做 a -> b,例如圖1中有 C1 -> B1。經過該定義,事件集中Lamport時間戳不等的事件可進行比較,咱們得到事件的偏序關係(partial order)。

若是C(a) = C(b),那a、b事件的順序又是怎樣的?假設a、b分別在節點P、Q上發生,Pi、Qj分別表示咱們給P、Q的編號,若是 C(a) = C(b) 而且 Pi <Qj,一樣定義爲a發生在b以前,記做 a => b。假如咱們對圖1的A、B、C分別編號Ai = 一、Bj = 二、Ck = 3,因 C(B4) = C(C3) 而且 Bj < Ck,則 B4 => C3。

經過以上定義,咱們能夠對全部事件排序、得到事件的全序關係(total order)。上圖例子,咱們能夠從C1到A4進行排序。

Vector clock

Lamport時間戳幫助咱們獲得事件順序關係,但還有一種順序關係不能用Lamport時間戳很好地表示出來,那就是同時發生關係(concurrent)[4]。例如圖1中事件B4和事件C3沒有因果關係,屬於同時發生事件,但Lamport時間戳定義二者有前後順序。

Vector clock是在Lamport時間戳基礎上演進的另外一種邏輯時鐘方法,它經過vector結構不但記錄本節點的Lamport時間戳,同時也記錄了其餘節點的Lamport時間戳5。Vector clock的原理與Lamport時間戳相似,使用圖例以下:

圖2: Vector clock space time (圖片來源: wikipedia)

假設有事件a、b分別在節點P、Q上發生,Vector clock分別爲Ta、Tb,若是 Tb[Q] > Ta[Q] 而且 Tb[P] >= Ta[P],則a發生於b以前,記做 a -> b。到目前爲止還和Lamport時間戳差異不大,那Vector clock怎麼判別同時發生關係呢?

若是 Tb[Q] > Ta[Q] 而且 Tb[P] < Ta[P],則認爲a、b同時發生,記做 a <-> b。例如圖2中節點B上的第4個事件 (A:2,B:4,C:1) 與節點C上的第2個事件 (B:3,C:2) 沒有因果關係、屬於同時發生事件。

Version vector

基於Vector clock咱們能夠得到任意兩個事件的順序關係,結果或爲前後順序或爲同時發生,識別事件順序在工程實踐中有很重要的引伸應用,最多見的應用是發現數據衝突(detect conflict)。

分佈式系統中數據通常存在多個副本(replication),多個副本可能被同時更新,這會引發副本間數據不一致[7],Version vector的實現與Vector clock很是相似[8],目的用於發現數據衝突[9]。下面經過一個例子說明Version vector的用法[10]:

圖3: Version vector

client端寫入數據,該請求被Sx處理並建立相應的vector ([Sx, 1]),記爲數據D1
第2次請求也被Sx處理,數據修改成D2,vector修改成([Sx, 2])
第三、第4次請求分別被Sy、Sz處理,client端先讀取到D2,而後D三、D4被寫入Sy、Sz
第5次更新時client端讀取到D二、D3和D4 3個數據版本,經過相似Vector clock判斷同時發生關係的方法可判斷D三、D4存在數據衝突,最終經過必定方法解決數據衝突並寫入D5
Vector clock只用於發現數據衝突,不能解決數據衝突。如何解決數據衝突因場景而異,具體方法有以最後更新爲準(last write win),或將衝突的數據交給client由client端決定如何處理,或經過quorum決議事先避免數據衝突的狀況發生[11]。

因爲記錄了全部數據在全部節點上的邏輯時鐘信息,Vector clock和Version vector在實際應用中可能面臨的一個問題是vector過大,用於數據管理的元數據(meta data)甚至大於數據自己[12]。

解決該問題的方法是使用server id取代client id建立vector (由於server的數量相對client穩定),或設定最大的size、若是超過該size值則淘汰最舊的vector信息10。

小結

以上介紹了分佈式系統裏邏輯時鐘的表示方法,經過Lamport timestamps能夠創建事件的全序關係,經過Vector clock能夠比較任意兩個事件的順序關係而且能表示無因果關係的事件,將Vector clock的方法用於發現數據版本衝突,因而有了Version vector。

分佈式系統理論基礎 - CAP

引言

CAP是分佈式系統、特別是分佈式存儲領域中被討論最多的理論,「什麼是CAP定理?」在Quora 分佈式系統分類下排名 FAQ 的 No.1。CAP在程序員中也有較廣的普及,它不只僅是「C、A、P不能同時知足,最多隻能3選2」,如下嘗試綜合各方觀點,從發展歷史、工程實踐等角度講述CAP理論。但願你們透過本文對CAP理論有更多地瞭解和認識。

CAP定理

CAP由Eric Brewer在2000年PODC會議上提出1,是Eric Brewer在Inktomi[3]期間研發搜索引擎、分佈式web緩存時得出的關於數據一致性(consistency)、服務可用性(availability)、分區容錯性(partition-tolerance)的猜測:

It is impossible for a web service to provide the three following guarantees : Consistency, Availability and Partition-tolerance.
該猜測在提出兩年後被證實成立[4],成爲咱們熟知的CAP定理:

數據一致性(consistency):若是系統對一個寫操做返回成功,那麼以後的讀請求都必須讀到這個新數據;若是返回失敗,那麼全部讀操做都不能讀到這個數據,對調用者而言數據具備強一致性(strong consistency) (又叫原子性 atomic、線性一致性 linearizable consistency)[5]
服務可用性(availability):全部讀寫請求在必定時間內獲得響應,可終止、不會一直等待
分區容錯性(partition-tolerance):在網絡分區的狀況下,被分隔的節點仍能正常對外服務
在某時刻若是知足AP,分隔的節點同時對外服務但不能相互通訊,將致使狀態不一致,即不能知足C;若是知足CP,網絡分區的狀況下爲達成C,請求只能一直等待,即不知足A;若是要知足CA,在必定時間內要達到節點狀態一致,要求不能出現網絡分區,則不能知足P。

C、A、P三者最多隻能知足其中兩個,和FLP定理同樣,CAP定理也指示了一個不可達的結果(impossibility result)。

CAP的工程啓示

CAP理論提出七、8年後,NoSql圈將CAP理論看成對抗傳統關係型數據庫的依據、闡明本身放寬對數據一致性(consistency)要求的正確性[6],隨後引發了大範圍關於CAP理論的討論。

CAP理論看似給咱們出了一道3選2的選擇題,但在工程實踐中存在不少現實限制條件,須要咱們作更多地考量與權衡,避免進入CAP認識誤區[7]。

一、關於 P 的理解

Partition字面意思是網絡分區,即因網絡因素將系統分隔爲多個單獨的部分,有人可能會說,網絡分區的狀況發生機率很是小啊,是否是不用考慮P,保證CA就好[8]。要理解P,咱們看回CAP證實[4]中P的定義:

In order to model partition tolerance, the network will be allowed to lose arbitrarily many messages sent from one node to another.
網絡分區的狀況符合該定義,網絡丟包的狀況也符合以上定義,另外節點宕機,其餘節點發往宕機節點的包也將丟失,這種狀況一樣符合定義。現實狀況下咱們面對的是一個不可靠的網絡、有必定機率宕機的設備,這兩個因素都會致使Partition,於是分佈式系統實現中 P 是一個必須項,而不是可選項9。

對於分佈式系統工程實踐,CAP理論更合適的描述是:在知足分區容錯的前提下,沒有算法能同時知足數據一致性和服務可用性[11]:

In a network subject to communication failures, it is impossible for any web service to implement an atomic read/write shared memory that guarantees a response to every request.
二、CA非0/1的選擇

P 是必選項,那3選2的選擇題不就變成數據一致性(consistency)、服務可用性(availability) 2選1?工程實踐中一致性有不一樣程度,可用性也有不一樣等級,在保證分區容錯性的前提下,放寬約束後能夠兼顧一致性和可用性,二者不是非此即彼[12]。

CAP定理證實中的一致性指強一致性,強一致性要求多節點組成的被調要能像單節點同樣運做、操做具有原子性,數據在時間、時序上都有要求。若是放寬這些要求,還有其餘一致性類型:

序列一致性(sequential consistency)[13]:不要求時序一致,A操做先於B操做,在B操做後若是全部調用端讀操做獲得A操做的結果,知足序列一致性
最終一致性(eventual consistency)[14]:放寬對時間的要求,在被調完成操做響應後的某個時間點,被調多個節點的數據最終達成一致
可用性在CAP定理裏指全部讀寫操做必需要能終止,實際應用中從主調、被調兩個不一樣的視角,可用性具備不一樣的含義。當P(網絡分區)出現時,主調能夠只支持讀操做,經過犧牲部分可用性達成數據一致。

工程實踐中,較常見的作法是經過異步拷貝副本(asynchronous replication)、quorum/NRW,實如今調用端看來數據強一致、被調端最終一致,在調用端看來服務可用、被調端容許部分節點不可用(或被網絡分隔)的效果[15]。

三、跳出CAP

CAP理論對實現分佈式系統具備指導意義,但CAP理論並無涵蓋分佈式工程實踐中的全部重要因素。

例如延時(latency),它是衡量系統可用性、與用戶體驗直接相關的一項重要指標[16]。CAP理論中的可用性要求操做能終止、不無休止地進行,除此以外,咱們還關心到底須要多長時間能結束操做,這就是延時,它值得咱們設計、實現分佈式系統時單列出來考慮。

延時與數據一致性也是一對「冤家」,若是要達到強一致性、多個副本數據一致,必然增長延時。加上延時的考量,咱們獲得一個CAP理論的修改版本PACELC[17]:若是出現P(網絡分區),如何在A(服務可用性)、C(數據一致性)之間選擇;不然,如何在L(延時)、C(數據一致性)之間選擇。

小結

以上介紹了CAP理論的源起和發展,介紹了CAP理論給分佈式系統工程實踐帶來的啓示。

CAP理論對分佈式系統實現有很是重大的影響,咱們能夠根據自身的業務特色,在數據一致性和服務可用性之間做出傾向性地選擇。經過放鬆約束條件,咱們能夠實如今不一樣時間點知足CAP(此CAP非CAP定理中的CAP,如C替換爲最終一致性)18[20]。

有很是很是多文章討論和研究CAP理論,但願這篇對你認識和了解CAP理論有幫助。

分佈式系統理論進階 - Paxos

引言

《分佈式系統理論基礎 - 一致性、2PC和3PC》一文介紹了一致性、達成一致性須要面臨的各類問題以及2PC、3PC模型,Paxos協議在節點宕機恢復、消息無序或丟失、網絡分化的場景下能保證決議的一致性,是被討論最普遍的一致性協議。

Paxos協議同時又以其「艱深晦澀」著稱,下面結合 Paxos Made Simple、The Part-Time Parliament 兩篇論文,嘗試經過Paxos推演、學習和了解Paxos協議。

Basic Paxos

何爲一致性問題?簡單而言,一致性問題是在節點宕機、消息無序等場景可能出現的狀況下,相互獨立的節點之間如何達成決議的問題,做爲解決一致性問題的協議,Paxos的核心是節點間如何肯定並只肯定一個值(value)。

也許你會疑惑只肯定一個值能起什麼做用,在Paxos協議裏肯定並只肯定一個值是肯定多值的基礎,如何肯定多值將在第二部分Multi Paxos中介紹,這部分咱們聚焦在「Paxos如何肯定並只肯定一個值」這一問題上。

和2PC相似,Paxos先把節點分紅兩類,發起提議(proposal)的一方爲proposer,參與決議的一方爲acceptor。假如只有一個proposer發起提議,而且節點不宕機、消息不丟包,那麼acceptor作到如下這點就能夠肯定一個值:

P1. 一個acceptor接受它收到的第一項提議
固然上面要求的前提條件有些嚴苛,節點不能宕機、消息不能丟包,還只能由一個proposer發起提議。咱們嘗試放寬條件,假設多個proposer能夠同時發起提議,又怎樣才能作到肯定並只肯定一個值呢?

首先proposer和acceptor須要知足如下兩個條件:

  1. proposer發起的每項提議分別用一個ID標識,提議的組成所以變爲(ID, value)
  2. acceptor能夠接受(accept)不止一項提議,當多數(quorum) acceptor接受一項提議時該提議被肯定(chosen)

(注: 注意以上「接受」和「肯定」的區別)

咱們約定後面發起的提議的ID比前面提議的ID大,並假設能夠有多項提議被肯定,爲作到肯定並只肯定一個值acceptor要作到如下這點:

P2. 若是一項值爲v的提議被肯定,那麼後續只肯定值爲v的提議
(注: 乍看這個條件不太好理解,謹記目標是「肯定並只肯定一個值」)

因爲一項提議被肯定(chosen)前必須先被多數派acceptor接受(accepted),爲實現P2,實質上acceptor須要作到:

P2a. 若是一項值爲v的提議被肯定,那麼acceptor後續只接受值爲v的提議
知足P2a則P2成立 (P2a => P2)。

目前在多個proposer能夠同時發起提議的狀況下,知足P一、P2a即能作到肯定並只肯定一個值。若是再加上節點宕機恢復、消息丟包的考量呢?

假設acceptor c 宕機一段時間後恢復,c 宕機期間其餘acceptor已經肯定了一項值爲v的決議但c 由於宕機並不知曉;c 恢復後若是有proposer立刻發起一項值不是v的提議,因爲條件P1,c 會接受該提議,這與P2a矛盾。爲了不這樣的狀況出現,進一步地咱們對proposer做約束:

P2b. 若是一項值爲v的提議被肯定,那麼proposer後續只發起值爲v的提議
知足P2b則P2a成立 (P2b => P2a => P2)。

P2b約束的是提議被肯定(chosen)後proposer的行爲,咱們更關心提議被肯定前proposer應該怎麼作:

P2c. 對於提議(n,v),acceptor的多數派S中,若是存在acceptor最近一次(即ID值最大)接受的提議的值爲v',那麼要求v = v';不然v可爲任意值
知足P2c則P2b成立 (P2c => P2b => P2a => P2)。

條件P2c是Basic Paxos的核心,光看P2c的描述可能會以爲一頭霧水,咱們經過 The Part-Time Parliament 中的例子加深理解:

假設有A~E 5個acceptor,- 表示acceptor因宕機等緣由缺席當次決議,x 表示acceptor不接受提議,o 表示接受提議;多數派acceptor接受提議後提議被肯定,以上表格對應的決議過程以下:

ID爲2的提議最先提出,根據P2c其提議值可爲任意值,這裏假設爲a
acceptor A/B/C/E 在以前的決議中沒有接受(accept)任何提議,於是ID爲5的提議的值也能夠爲任意值,這裏假設爲b
acceptor B/D/E,其中D曾接受ID爲2的提議,根據P2c,該輪ID爲14的提議的值必須與ID爲2的提議的值相同,爲a
acceptor A/C/D,其中D曾接受ID爲2的提議、C曾接受ID爲5的提議,相比之下ID 5較ID 2大,根據P2c,該輪ID爲27的提議的值必須與ID爲5的提議的值相同,爲b;該輪決議被多數派acceptor接受,所以該輪決議得以肯定
acceptor B/C/D,3個acceptor以前都接受過提議,相比之下C、D曾接受的ID 27的ID號最大,該輪ID爲29的提議的值必須與ID爲27的提議的值相同,爲b
以上提到的各項約束條件能夠概括爲3點,若是proposer/acceptor知足下面3點,那麼在少數節點宕機、網絡分化隔離的狀況下,在「肯定並只肯定一個值」這件事情上能夠保證一致性(consistency):

B1(ß): ß中每一輪決議都有惟一的ID標識
B2(ß): 若是決議B被acceptor多數派接受,則肯定決議B
B3(ß): 對於ß中的任意提議B(n,v),acceptor的多數派中若是存在acceptor最近一次(即ID值最大)接受的提議的值爲v',那麼要求v = v';不然v可爲任意值
(注: 希臘字母ß表示多輪決議的集合,字母B表示一輪決議)

另外爲保證P2c,咱們對acceptor做兩個要求:

  1. 記錄曾接受的ID最大的提議,因proposer須要問詢該信息以決定提議值
  2. 在迴應提議ID爲n的proposer本身曾接受過ID最大的提議時,acceptor同時保證(promise)再也不接受ID小於n的提議

至此,proposer/acceptor完成一輪決議可概括爲prepare和accept兩個階段。prepare階段proposer發起提議問詢提議值、acceptor迴應問詢並進行promise;accept階段完成決議,圖示以下:

還有一個問題須要考量,假如proposer A發起ID爲n的提議,在提議未完成前proposer B又發起ID爲n+1的提議,在n+1提議未完成前proposer C又發起ID爲n+2的提議…… 如此acceptor不能完成決議、造成活鎖(livelock),雖然這不影響一致性,但咱們通常不想讓這樣的狀況發生。解決的方法是從proposer中選出一個leader,提議統一由leader發起。

最後咱們再引入一個新的角色:learner,learner依附於acceptor,用於習得已肯定的決議。以上決議過程都只要求acceptor多數派參與,而咱們但願儘可能全部acceptor的狀態一致。若是部分acceptor因宕機等緣由未知曉已肯定決議,宕機恢復後可經本機learner採用pull的方式從其餘acceptor習得。

Multi Paxos

經過以上步驟分佈式系統已經能肯定一個值,「只肯定一個值有什麼用?這可解決不了我面臨的問題。」 你心中可能有這樣的疑問。

其實不斷地進行「肯定一個值」的過程、再爲每一個過程編上序號,就能獲得具備全序關係(total order)的系列值,進而能應用在數據庫副本存儲等不少場景。咱們把單次「肯定一個值」的過程稱爲實例(instance),它由proposer/acceptor/learner組成,下圖說明了A/B/C三機上的實例:

不一樣序號的實例之間互相不影響,A/B/C三機輸入相同、過程實質等同於執行相同序列的狀態機(state machine)指令 ,於是將獲得一致的結果。

proposer leader在Multi Paxos中還有助於提高性能,常態下統一由leader發起提議,可節省prepare步驟(leader不用問詢acceptor曾接受過的ID最大的提議、只有leader提議也不須要acceptor進行promise)直至發生leader宕機、從新選主。

小結

以上介紹了Paxos的推演過程、如何在Basic Paxos的基礎上經過狀態機構建Multi Paxos。Paxos協議比較「艱深晦澀」,但多讀幾遍論文通常能理解其內涵,更難的是如何將Paxos真正應用到工程實踐。

微信後臺開發同窗實現並開源了一套基於Paxos協議的多機狀態拷貝類庫PhxPaxos,PhxPaxos用於將單機服務擴展到多機,其通過線上系統驗證並在一致性保證、性能等方面做了不少考量。

分佈式系統理論進階 - Raft、Zab

引言

《分佈式系統理論進階 - Paxos》介紹了一致性協議Paxos,今天咱們來學習另外兩個常見的一致性協議——Raft和Zab。經過與Paxos對比,瞭解Raft和Zab的核心思想、加深對一致性協議的認識。

Raft

Paxos偏向於理論、對如何應用到工程實踐說起較少。理解的難度加上現實的骨感,在生產環境中基於Paxos實現一個正確的分佈式系統很是難[1]:

There are significant gaps between the description of the Paxos algorithm and the needs of a real-world system. In order to build a real-world system, an expert needs to use numerous ideas scattered in the literature and make several relatively small protocol extensions. The cumulative effort will be substantial and the final system will be based on an unproven protocol.

Raft2在2013年提出,提出的時間雖然不長,但已經有不少系統基於Raft實現。相比Paxos,Raft的買點就是更利於理解、更易於實行。

爲達到更容易理解和實行的目的,Raft將問題分解和具體化:Leader統一處理變動操做請求,一致性協議的做用具化爲保證節點間操做日誌副本(log replication)一致,以term做爲邏輯時鐘(logical clock)保證時序,節點運行相同狀態機(state machine)[4]獲得一致結果。Raft協議具體過程以下:

Client發起請求,每一條請求包含操做指令
請求交由Leader處理,Leader將操做指令(entry)追加(append)至操做日誌,緊接着對Follower發起AppendEntries請求、嘗試讓操做日誌副本在Follower落地
若是Follower多數派(quorum)贊成AppendEntries請求,Leader進行commit操做、把指令交由狀態機處理
狀態機處理完成後將結果返回給Client
指令經過log index(指令id)和term number保證時序,正常狀況下Leader、Follower狀態機按相同順序執行指令,得出相同結果、狀態一致。

宕機、網絡分化等狀況可引發Leader從新選舉(每次選舉產生新Leader的同時,產生新的term)、Leader/Follower間狀態不一致。Raft中Leader爲本身和全部Follower各維護一個nextIndex值,其表示Leader緊接下來要處理的指令id以及將要發給Follower的指令id,LnextIndex不等於FnextIndex時表明Leader操做日誌和Follower操做日誌存在不一致,這時將從Follower操做日誌中最初不一致的地方開始,由Leader操做日誌覆蓋Follower,直到LnextIndex、FnextIndex相等。

Paxos中Leader的存在是爲了提高決議效率,Leader的有無和數目並不影響決議一致性,Raft要求具有惟一Leader,並把一致性問題具體化爲保持日誌副本的一致性,以此實現相較Paxos而言更容易理解、更容易實現的目標。

Zab

Zab5的全稱是Zookeeper atomic broadcast protocol,是Zookeeper內部用到的一致性協議。相比Paxos,Zab最大的特色是保證強一致性(strong consistency,或叫線性一致性linearizable consistency)。

和Raft同樣,Zab要求惟一Leader參與決議,Zab能夠分解成discovery、sync、broadcast三個階段:

discovery: 選舉產生PL(prospective leader),PL收集Follower epoch(cepoch),根據Follower的反饋PL產生newepoch(每次選舉產生新Leader的同時產生新epoch,相似Raft的term)
sync: PL補齊相比Follower多數派缺失的狀態、以後各Follower再補齊相比PL缺失的狀態,PL和Follower完成狀態同步後PL變爲正式Leader(established leader)
broadcast: Leader處理Client的寫操做,並將狀態變動廣播至Follower,Follower多數派經過以後Leader發起將狀態變動落地(deliver/commit)
Leader和Follower之間經過心跳判別健康狀態,正常狀況下Zab處在broadcast階段,出現Leader宕機、網絡隔離等異常狀況時Zab從新回到discovery階段。

瞭解完Zab的基本原理,咱們再來看Zab怎樣保證強一致性,Zab經過約束事務前後順序達到強一致性,先廣播的事務先commit、FIFO,Zab稱之爲primary order(如下簡稱PO)。實現PO的核心是zxid。

Zab中每一個事務對應一個zxid,它由兩部分組成:<e, c>,e即Leader選舉時生成的epoch,c表示當次epoch內事務的編號、依次遞增。假設有兩個事務的zxid分別是z、z',當知足 z.e < z'.e 或者 z.e = z'.e && z.c < z'.c 時,定義z先於z'發生(z < z')。

爲實現PO,Zab對Follower、Leader有如下約束:

有事務z和z',若是Leader先廣播z,則Follower需保證先commit z對應的事務
有事務z和z',z由Leader p廣播,z'由Leader q廣播,Leader p先於Leader q,則Follower需保證先commit z對應的事務
有事務z和z',z由Leader p廣播,z'由Leader q廣播,Leader p先於Leader q,若是Follower已經commit z,則q需保證已commit z才能廣播z'
第一、2點保證事務FIFO,第3點保證Leader上具有全部已commit的事務。

相比Paxos,Zab約束了事務順序、適用於有強一致性需求的場景。

Paxos、Raft、Zab再比較

除Paxos、Raft和Zab外,Viewstamped Replication(簡稱VR)7也是討論比較多的一致性協議。這些協議包含不少共同的內容(Leader、quorum、state machine等),於是咱們不由要問:Paxos、Raft、Zab和VR等分佈式一致性協議區別到底在哪,仍是根本就是一回事?[9]

Paxos、Raft、Zab和VR都是解決一致性問題的協議,Paxos協議原文傾向於理論,Raft、Zab、VR傾向於實踐,一致性保證程度等的不一樣也致使這些協議間存在差別。下圖幫助咱們理解這些協議的類似點和區別[10]:

相比Raft、Zab、VR,Paxos更純粹、更接近一致性問題本源,儘管Paxos傾向理論,但不表明Paxos不能應用於工程。基於Paxos的工程實踐,須考慮具體需求場景(如一致性要達到什麼程度),再在Paxos原始語意上進行包裝。

小結

以上介紹分佈式一致性協議Raft、Zab的核心思想,分析Raft、Zab與Paxos的異同。實現分佈式系統時,先從具體需求和場景考慮,Raft、Zab、VR、Paxos等協議沒有絕對地好與很差,只是適不適合。

分佈式系統理論進階 - Paxos變種和優化

引言

《分佈式系統理論進階 - Paxos》中咱們瞭解了Basic Paxos、Multi Paxos的基本原理,但若是想把Paxos應用於工程實踐,瞭解基本原理還不夠。

有不少基於Paxos的優化,在保證一致性協議正確(safety)的前提下,減小Paxos決議通訊步驟、避免單點故障、實現節點負載均衡,從而下降時延、增長吞吐量、提高可用性,下面咱們就來了解這些Paxos變種。

Multi Paxos

首先咱們來回顧一下Multi Paxos,Multi Paxos在Basic Paxos的基礎上肯定一系列值,其決議過程以下:

phase1a: leader提交提議給acceptor

phase1b: acceptor返回最近一次接受的提議(即曾接受的最大的提議ID和對應的value),未接受過提議則返回空

phase2a: leader收集acceptor的應答,分兩種狀況處理

phase2a.1: 若是應答內容都爲空,則自由選擇一個提議value

phase2a.2: 若是應答內容不爲空,則選擇應答裏面ID最大的提議的value

phase2b: acceptor將決議同步給learner

Multi Paxos中leader用於避免活鎖,但leader的存在會帶來其餘問題,一是如何選舉和保持惟一leader(雖然無leader或多leader不影響一致性,但影響決議進程progress),二是充當leader的節點會承擔更多壓力,如何均衡節點的負載。Mencius[1]提出節點輪流擔任leader,以達到均衡負載的目的;租約(lease)能夠幫助實現惟一leader,但leader故障狀況下可致使服務短時間不可用。

Fast Paxos

在Multi Paxos中,proposer -> leader -> acceptor -> learner,從提議到完成決議共通過3次通訊,能不能減小通訊步驟?

對Multi Paxos phase2a,若是能夠自由提議value,則可讓proposer直接發起提議、leader退出通訊過程,變爲proposer -> acceptor -> learner,這就是Fast Paxos[2]的由來。

Multi Paxos裏提議都由leader提出,於是不存在一次決議出現多個value,Fast Paxos裏由proposer直接提議,一次決議裏可能有多個proposer提議、出現多個value,即出現提議衝突(collision)。leader起到初始化決議進程(progress)和解決衝突的做用,當衝突發生時leader從新參與決議過程、回退到3次通訊步驟。

Paxos自身隱含的一個特性也能夠達到減小通訊步驟的目標,若是acceptor上一次肯定(chosen)的提議來自proposerA,則當次決議proposerA能夠直接提議減小一次通訊步驟。若是想實現這樣的效果,須要在proposer、acceptor記錄上一次決議肯定(chosen)的歷史,用以在提議前知道哪一個proposer的提議上一次被肯定、當次決議能不能節省一次通訊步驟。

EPaxos

除了從減小通訊步驟的角度提升Paxos決議效率外,還有其餘方面能夠下降Paxos決議時延,好比Generalized Paxos[3]提出不衝突的提議(例如對不一樣key的寫請求)能夠同時決議、以下降Paxos時延。

更進一步地,EPaxos4提出一種既支持不衝突提議同時提交下降時延、還均衡各節點負載、同時將通訊步驟減小到最少的Paxos優化方法。

爲達到這些目標,EPaxos的實現有幾個要點。一是EPaxos中沒有全局的leader,而是每一次提議發起提議的proposer做爲當次提議的leader(command leader);二是不相互影響(interfere)的提議能夠同時提交;三是跳過prepare,直接進入accept階段。EPaxos決議的過程以下:

左側展現了互不影響的兩個update請求的決議過程,右側展現了相互影響的兩個update請求的決議。Multi Paxos、Mencius、EPaxos時延和吞吐量對比:

爲判斷決議是否相互影響,實現EPaxos得記錄決議之間的依賴關係。

小結

以上介紹了幾個基於Paxos的變種,Mencius中節點輪流作leader、均衡節點負載,Fast Paxos減小一次通訊步驟,Generalized Paxos容許互不影響的決議同時進行,EPaxos無全局leader、各節點平等分擔負載。

優化無止境,對Paxos也同樣,應用在不一樣場景和不一樣範圍的Paxos變種和優化將繼續不斷出現。

相關文章
相關標籤/搜索