分布式系统:一致性协议
一致性模型本質上是進程與數據存儲的約定,通過一致性模型我們可以理解和推理在分布式系統中數據復制需要考慮的問題和基本假設。那么,一致性模型的具體實現有一些呢?本文會介紹一致性協議實現的主要思想和方法。
什么是一致性協議
一致性協議描述了特定一致性模型的實際實現。一致性模型就像是接口,而一致性協議就像是接口的具體實現。一致性模型提供了分布式系統中數據復制時保持一致性的約束,為了實現一致性模型的約束,需要通過一致性協議來保證。
一致性協議根據是否允許數據分歧可以分為兩種:
- 單主協議(不允許數據分歧):整個分布式系統就像一個單體系統,所有寫操作都由主節點處理并且同步給其他副本。例如主備同步、2PC、Paxos 都屬于這類協議。
- 多主協議(允許數據分歧):所有寫操作可以由不同節點發起,并且同步給其他副本。例如 Gossip、POW。
可以發現,它們的核心區別在于是否允許多個節點發起寫操作,單主協議只允許由主節點發起寫操作,因此它可以保證操作有序性,一致性更強。而多主協議允許多個節點發起寫操作,因此它不能保證操作的有序性,只能做到弱一致性。
值得注意的是,一致性協議的分類方式有很多種,主要是看從哪個角度出發進行歸類,常用的另一個歸類方式是根據同步/異步復制來劃分,這里就不多做討論了。下面對單主協議和多主協議分別做一些共性的分析,篇幅所限,不會深入到協議細節。
單主協議
單主協議的共同點在于都會用一個主節點來負責寫操作,這樣能夠保證全局寫的順序一致性,它有另一個名字叫定序器,非常的形象。
主備復制
主備復制可以說是最常用的數據復制方法,也是最基礎的方法,很多其他協議都是基于它的變種。?主備復制要求所有的寫操作都在主節點上進行,然后將操作的日志發送給其他副本。可以發現由于主備復制是有延遲的,所以它實現的是最終一致性。
主備復制的實現方式:主節點處理完寫操作之后立即返回結果給客戶端,寫操作的日志異步同步給其他副本。這樣的好處是性能高,客戶端不需要等待數據同步,缺點是如果主節點同步數據給副本之前數據缺失了,那么這些數據就永久丟失了。MySQL 的主備同步就是典型的異步復制。
兩階段提交
兩階段提交(2PC)是關系型數據庫常用的保持分布式事務一致性的協議,它也屬于同步復制協議,即數據都同步完成之后才返回客戶端結果。可以發現 2PC 保證所有節點數據一致之后才返回給客戶端,實現了順序一致性。
2PC 把數據復制分為兩步:
可以發現 2PC 是典型的 CA 系統,為了保證一致性和可用性,2PC 一旦出現網絡分區或者節點不可用就會被拒絕寫操作,把系統變成只讀的。由于 2PC 容易出現節點宕機導致一直阻塞的情況,所以在數據復制的場景中不常用,一般多用于分布式事務中(注:實際應用過程中會有很多優化)。
分區容忍的一致性協議
分區容忍的一致性協議跟所有的單主協議一樣,它也是只有一個主節點負責寫入(提供順序一致性),但它跟 2PC 的區別在于它只需要保證大多數節點(一般是超過半數)達成一致就可以返回客戶端結果,這樣可以提高了性能,同時也能容忍網絡分區(少數節點分區不會導致整個系統無法運行)。分區容忍的一致性算法保證大多數節點數據一致后才返回客戶端,同樣實現了順序一致性。
下面用一個簡單的示例來說明這類算法的核心思想。假設現在有一個分布式文件系統,它的文件都被復制到 3 個服務器上,我們規定:要更新一個文件,客戶端必須先訪問至少 2 個服務器(大多數),得到它們同意之后才能執行更新,同時每個文件都會有版本號標識;要讀取文件的時候,客戶端也必須要訪問至少 2 個服務器獲取該文件的版本號,如果所有的版本號一致,那么該版本必定是最新的版本,因為如果前面的更新操作要求必須要有大多數服務器的同意才能更新文件。
以上就是我們熟知的 Paxos、ZAB、Raft 等分區容忍的一致性協議的核心思想:一致性的保證不一定非要所有節點都保持一致,只要大多數節點更新了,對于整個分布式系統來說數據也是一致性的。上面只是一個簡單的闡述,真正的算法實現是比較復雜的,這里就不展開了。
分區容忍的一致性協議如 Paxos 是典型的 CP 系統,為了保證一致性和分區容忍,在網絡分區的情況下,允許大多數節點的寫入,通過大多數節點的一致性實現整個系統的一致性,同時讓少數節點停止服務(不能讀寫),放棄整體系統的可用性,也就是說客戶端訪問到少數節點時會失敗。
值得注意的是,根據 CAP 理論,假設現在有三個節點 A、B、C,當 C 被網絡分區時,有查詢請求過來,此時 C 因為不能和其他節點通信,所以 C 無法對查詢做出響應,也就不具備可用性。但在工程實現上,這個問題是可以被繞過的,當客戶端訪問 C 無法得到響應時,它可以去訪問 A、B,實際上對于整個系統來說還是部分可用性的,并不是說 CP 的系統一定就失去可用性。詳細的分析參考分布式系統:CAP 理論的前世今生
多主協議
相比單主協議為了實現順序一致性,不允許多個節點并發寫,多主協議恰恰相反,只保證最終一致性,允許多個節點并發寫,能夠顯著提升系統性能。由于多主協議一般提供的都是最終一致性,所以常用在對數據一致性要求不高的場景中。
Gossip 協議就是一種典型的多主協議,很多分布式系統都使用它來做數據復制,例如比特幣,作為一條去中心化的公鏈,所有節點的數據同步都用的是 Gossip 協議。此外,Gossip 協議也在一些分布式數據庫中如 Dynamo 中被用來做分布式故障檢測的狀態同步,當有節點故障離開集群時,其他節點可以快速檢測到。
從名稱上就可以看出 Gossip 協議的核心思想,Gossip 是流言八卦的意思,想想我們日常生活人與人之間傳八卦的場景,在學校里面一個八卦一旦有一個人知道了,通過人傳人,基本上整個學校的人最終都會知道了。因此 Gossip 協議的核心思想就是:每個節點都可以對其他節點發送消息,接收到消息的節點隨機選擇其他節點發送消息,接收到消息的節點也做同樣的事情。
多主協議允許運行多個節點并發寫,就一定會出現對一個數據并發寫導致數據沖突的情況,因此這類協議都需要解決并發寫的問題。單主協議通過主節點控制寫入,保證不會出現并發寫的情況,因為所有寫操作最終都會通過主節點排序,從某種意義上講,使用單主協議的系統對于寫入實際上是串行的,因此其性能是有瓶頸的。而多主協議允許多節點并發寫,提搞了寫入的性能,但是實際上它是把數據合并的操作延遲了,單主協議在寫入的時候就進行了數據合并,因此讀取數據的時候如果出現數據沖突的時候,就需要對數據進行合并,保證全局一致性。
前面我們提到比特幣使用的是 Gossip 協議做數據復制,那么問題來了,不是說多主協議性能會比較高嗎,為什么比特幣的性能那么差?這里實際上要分開來看,由于比特幣是去中心化的,但是它的支付功能需要保證全局數據一致性,因此它用了一種很巧妙的一致性算法 POW:所有節點都做一道數學題,誰先算出答案誰有權利將交易寫到鏈上,然后利用 Gossip 協議傳播它的答案和交易,其他節點驗證它的答案正確就將數據保存起來。
到這里你可能會有一個疑問:POW 作為多主協議為什么性能這么低?任何協議都有它適用的場景。在比特幣這個場景中,它對于數據一致性是有強需求的,理論上用單主協議是最優的選擇。但是比特幣作為去中心化的數字貨幣是不會使用單主協議的,否則又變成中心化的系統了。因此比特幣只能選擇多主協議,通過 POW 協議將比特幣整條鏈操作進行了近似串行化,這樣才能降低出現雙花的概率(并發寫的時候一個比特幣被消費多次),魚與熊掌不可兼得,既然要強一致性,那么只能犧牲性能來換取。
由于多主協議允許了數據分歧,那么就需要有解決數據沖突的策略來保證最終一致性。如果要嚴格區分的話,比特幣實際上應用了兩個一致性協議:
- POW:決定節點的記賬權,起到類似單主協議中定序器的作用。注意 POW 也是多主協議,盡管概率很低,但是它有可能出現多個節點同時算出答案,一起出塊(并發寫)的情況,此時我們稱比特幣出現了分叉,即出現了數據沖突。
- Gossip:用于將出塊的交易同步到全球所有節點。由于 POW 會出現并發寫的情況,當一個節點同時接受到多個節點寫入請求時,就需要解決數據沖突的問題。比特幣解決數據沖突的方式就是當出現分叉時,選取最長的那條鏈作為主鏈,其他分叉的鏈上的交易會被回滾,等待重新打包出塊。
總結
本文主要從是否允許數據分歧的角度將分布式一致性協議分為兩種:單主協議和多主協議。其中單主協議會用一個主節點來負責寫操作,這樣能夠保證全局寫的順序一致性,但因此也犧牲了一部分性能。而多主協議則允許寫操作可以由不同節點發起,并且同步給其他副本,只能保證最終一致性,但因此也提升了系統并發寫入的性能。對數據一致性要求高的場景例如分布式數據庫,主要會使用單主協議,對數據一致性要求不高例如故障檢測,主要會使用多主協議來提高性能,當然也有特例,像比特幣為了去中心化使用 POW 和 Gossip 結合進行數據復制。
值得注意的是,文中提到的單主、多主協議只是我個人對分布式一致性協議的一種分類方式,幫助我們更好的理解。讀者可以看一下參考資料,看一下不同作者對分布式協議是如何分類的,這樣對分布式一致性協議會有更深入的理解。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的分布式系统:一致性协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AnalyticDB for MySQL
- 下一篇: 那些年,我们见过的Java服务端乱象