分佈式系統的原理與設計

svhqdU.md.jpg

svhvW9.md.gif

  • 1 概念
    1.1 模型
    1.2 副本
    1.3 衡量分佈式系統的指標
  • 2 分佈式系統原理
    2.1 數據分佈方式
    2.2 基本副本協議
    2.3 Lease 機制
    2.4 Quorum 機制
    2.5 日誌技術
    2.6 兩階段提交協議
    2.7 MVCC
    2.8 Paxos協議
    2.9 CAP

1 概念

1.1 模型

節點
在具體的工程項目中,一個節點每每是一個操做系統上的進程。在本文的模型中,認爲節點是一個完整的、不可分的總體,若是某個程序進程實際上由若干相對獨立部分構成,則在模型中能夠將一個進程劃分爲多個節點。
異常php

  • 1.機器宕機:機器宕機是最多見的異常之一。在大型集羣中每日宕機發生的機率爲千分之一左右,在實踐中,一臺宕機的機器恢復的時間一般認爲是24 小時,通常須要人工介入重啓機器。node

  • 2.網絡異常:消息丟失,兩片節點之間彼此徹底沒法通訊,即出現了「網絡分化」;算法

    消息亂序,有必定的機率不是按照發送時的順序依次到達目的節點,考慮使用序列號等機制處理網絡消息的亂序問題,使得無效的、過時的網絡消息不影響系統的正確性;數據庫

    數據錯誤;不可靠的TCP,TCP 協議爲應用層提供了可靠的、面向鏈接的傳輸服務,但在分佈式系統的協議設計中不能認爲全部網絡通訊都基於TCP 協議則通訊就是可靠的。緩存

    TCP協議只能保證同一個TCP 連接內的網絡消息不亂序,TCP 連接之間的網絡消息順序則沒法保證。安全

  • 3.分佈式三態:若是某個節點向另外一個節點發起RPC(Remote procedure call)調用,即某個節點A 向另外一個節點B 發送一個消息,節點B 根據收到的消息內容完成某些操做,並將操做的結果經過另外一個消息返回給節點A,那麼這個RPC 執行的結果有三種狀態:「成功」、「失敗」、「超時(未知)」,稱之爲分佈式系統的三態。服務器

  • 4.存儲數據丟失:對於有狀態節點來講,數據丟失意味着狀態丟失,一般只能從其餘節點讀取、恢復存儲的狀態。網絡

  • 5.異常處理原則:被大量工程實踐所檢驗過的異常處理黃金原則是:任何在設計階段考慮到的異常狀況必定會在系統實際運行中發生,但在系統實際運行遇到的異常卻頗有可能在設計時未能考慮,因此,除非需求指標容許,在系統設計時不能放過任何異常狀況。session

1.2 副本

副本(replica/copy)指在分佈式系統中爲數據或服務提供的冗餘。對於數據副本指在不一樣的節點上持久化同一份數據,當出現某一個節點的存儲的數據丟失時,能夠從副本上讀到數據。數據結構

數據副本是分佈式系統解決數據丟失異常的惟一手段。另外一類副本是服務副本,指數個節點提供某種相同的服務,這種服務通常並不依賴於節點的本地存儲,其所需數據通常來自其餘節點。
副本協議是貫穿整個分佈式系統的理論核心。

副本一致性
分佈式系統經過副本控制協議,使得從系統外部讀取系統內部各個副本的數據在必定的約束條件下相同,稱之爲副本一致性(consistency)。副本一致性是針對分佈式系統而言的,不是針對某一個副本而言。

  • 1.強一致性(strong consistency):任什麼時候刻任何用戶或節點均可以讀到最近一次成功更新的副本數據。強一致性是程度最高的一致性要求,也是實踐中最難以實現的一致性。

  • 2.單調一致性(monotonic consistency):任什麼時候刻,任何用戶一旦讀到某個數據在某次更新後的值,這個用戶不會再讀到比這個值更舊的值。

    單調一致性是弱於強一致性卻很是實用的一種一致性級別。由於一般來講,用戶只關心從己方視角觀察到的一致性,而不會關注其餘用戶的一致性狀況。

  • 3.會話一致性(session consistency):任何用戶在某一次會話內一旦讀到某個數據在某次更新後的值,這個用戶在此次會話過程當中不會再讀到比這個值更舊的值。

    會話一致性經過引入會話的概念,在單調一致性的基礎上進一步放鬆約束,會話一致性只保證單個用戶單次會話內數據的單調修改,對於不一樣用戶間的一致性和同一用戶不一樣會話間的一致性沒有保障。

實 踐中有許多機制正好對應會話的概念,例如php 中的session 概念。

  • 4.最終一致性(eventual consistency):最終一致性要求一旦更新成功,各個副本上的數據最終將達 到徹底一致的狀態,但達到徹底一致狀態所須要的時間不能保障。

    對於最終一致性系統而言,一個用戶只要始終讀取某一個副本的數據,則能夠實現相似單調一致性的效果,但一旦用戶更換讀取的副本,則沒法保障任何一致性。

  • 5.弱一致性(week consistency):一旦某個更新成功,用戶沒法在一個肯定時間內讀到此次更新的值,且即便在某個副本上讀到了新的值,也不能保證在其餘副本上能夠讀到新的值。

    弱一致性系統通常很難在實際中使用,使用弱一致性系統須要應用方作更多的工做從而使得系統可用。

1.3 衡量分佈式系統的指標

  • 1.性能:系統的吞吐能力,指系統在某一時間能夠處理的數據總量,一般能夠用系統每秒處理的總的數據量來衡量;

    系統的響應延遲,指系統完成某一功能須要使用的時間;

    系統的併發能力,指系統能夠同時完成某一功能的能力,一般也用QPS(query per second)來衡量。

    上述三個性能指標每每會相互制約,追求高吞吐的系統,每每很難作到低延遲;系統平均響應時間較長時,也很難提升QPS。

  • 2.可用性:系統的可用性(availability)指系統在面對各類異常時能夠正確提供服務的能力。

    系統的可用性能夠用系統停服務的時間與正常服務的時間的比例來衡量,也能夠用某功能的失敗次數與成功次數的比例來衡量。可用性是分佈式的重要指標,衡量了系統的魯棒性,是系統容錯能力的體現。

  • 3.可擴展性:系統的可擴展性(scalability)指分佈式系統經過擴展集羣機器規模提升系統性能(吞吐、延遲、併發)、存儲容量、計算能力的特性。好的分佈式系統總在追求「線性擴展性」,也就是使得系統的某一指標能夠隨着集羣中的機器數量線性增加。

  • 4.一致性:分佈式系統爲了提升可用性,老是不可避免的使用副本的機制,從而引起副本一致性的問題。越是強的一致的性模型,對於用戶使用來講使用起來越簡單。

2 分佈式系統原理

  • 2.1 數據分佈方式

    所謂分佈式系統顧名思義就是利用多臺計算機協同解決單臺計算機所不能解決的計算、存儲等問題。

    單機系統與分佈式系統的最大的區別在於問題的規模,即計算、存儲的數據量的區別。

    將一個單機問題使用分佈式解決,首先要解決的就是如何將問題拆解爲可使用多機分佈式解決,使得分佈式系統中的每臺機器負責原問題的一個子集。因爲不管是計算仍是存儲,其問題輸入對象都是數據,因此如何拆解分佈式系統的輸入數據成爲分佈式系統的基本問題。

哈希方式
sv4UO0.md.jpg
哈希分佈數據的缺點一樣明顯,突出表現爲可擴展性不高,一旦集羣規模須要擴展,則幾乎全部的數據須要被遷移並從新分佈。工程中,擴展哈希分佈數據的系統時,每每使得集羣規模成倍擴展,按照數據從新計算哈希,這樣本來一臺機器上的數據只需遷移一半到另外一臺對應的機器上便可完成擴展。

針對哈希方式擴展性差的問題,一種思路是再也不簡單的將哈希值與機器作除法取模映射,而是將對應關係做爲元數據由專門的元數據服務器管理.同時,哈希值取模個數每每大於機器個數,這樣同一臺機器上須要負責多個哈希取模的餘數。但須要以較複雜的機制維護大量的元數據。哈希分佈數據的另外一個缺點是,一旦某數據特徵值的數據嚴重不均,容易出現「數據傾斜」(data skew)問題。

哈希分佈數據的另外一個缺點是,一旦某數據特徵值的數據嚴重不均,容易出現「數據傾斜」(data skew)問題
sv4dmV.md.jpg
按數據範圍分佈
按數據範圍分佈是另外一個常見的數據分佈式,將數據按特徵值的值域範圍劃分爲不一樣的區間,使得集羣中每臺(組)服務器處理不一樣區間的數據。
sv4wwT.md.jpg
工程中,爲了數據遷移等負載均衡操做的方便,每每利用動態劃分區間的技術,使得每一個區間中服務的數據量儘可能的同樣多。當某個區間的數據量較大時,經過將區間「分裂」的方式拆分爲兩個區間,使得每一個數據區間中的數據量都儘可能維持在一個較爲固定的閾值之下。

通常的,每每須要使用專門的服務器在內存中維護數據分佈信息,稱這種數據的分佈信息爲一種元信息。甚至對於大規模的集羣,因爲元信息的規模很是龐大,單臺 計算機沒法獨立維護,須要使用多臺機器做爲元信息服務器。

按數據量分佈
數據量分佈數據與具體的數據特徵無關,而是將數據視爲一個順序增加的文件,並將這個文件按照某一較爲固定的大小劃分爲若干數據塊(chunk),不一樣的數據塊分佈到不一樣的服務器上

與按數據範圍分佈數據的方式相似的是,按數據量分佈數據也須要記錄數據塊的具體分佈狀況,並將該分佈信息做爲元數據使用元數據服務器管理。

因爲與具體的數據內容無關,按數據量分佈數據的方式通常沒有數據傾斜的問題,數據老是被均勻切分並分佈到集羣中。

當集羣須要從新負載均衡時,只需經過遷移數據塊便可完成。集羣擴容也沒有太大的限制,只需將部分數據庫遷移到新加入的機器上便可以完成擴容。

按數據量劃分數據的缺點是須要管理較爲複雜的元信息,與按範圍分佈數據的方式相似,當集羣規模較大時,元信息的數據量也變得很大,高效的管理元信息成爲新的課題。

一致性哈希
一致性哈希(consistent hashing)是另外一個種在工程中使用較爲普遍的數據分佈方式。一致性哈希最初在P2P 網絡中做爲分佈式哈希表(DHT)的經常使用數據分佈算法。

一致性哈希的基本方式是使用一個哈希函數計算數據或數據特徵的哈希值,令該哈希函數的輸出值域爲一個封閉的環,即哈希函數輸出的最大值是最小值的前序。將節點隨機分佈到這個環上,每一個節點負責處理從本身開始順時針至下一個節點的所有哈希值域上的數據。
sv40TU.md.jpg
使用一致性哈希的方式須要將節點在一致性哈希環上的位置做爲元信息加以管理,這點比直接使用哈希分佈數據的方式要複雜。然而,節點的位置信息只於集羣中的機器規模相關,其元信息的量一般比按數據範圍分佈數據和按數據量分佈數據的元信息量要小不少。

爲此一種常見的改進算法是引入虛節點(virtual node)的概念,系統初始時就建立許多虛節點,虛節點的個數通常遠大於將來集羣中機器的個數,將虛節點均勻分佈到一致性哈希值域環上,其功能與基本一致性哈希算法中的節點相同。爲每一個節點分配若干虛節點。

操做數據時,首先經過數據的哈希值在環上找到對應的虛節點,進而查找元數據找到對應的真實節點。使用虛節點改進有多個優勢。

首先,一旦某個節點不可用,該節點將使得多個虛節點不可用,從而使得多個相鄰的真實節點負載失效節點的壓裏。同理,一旦加入一個新節點,能夠分配多個虛節點,從而使得新節點能夠 負載多個原有節點的壓力,從全局看,較容易實現擴容時的負載均衡。

副本與數據分佈
分佈式系統容錯、提升可用性的基本手段就是使用副本。對於數據副本的分佈方式主要影響系統的可擴展性。一種基本的數據副本策略是以機器爲單位,若干機器互爲副本,副本機器之間的數據徹底相同。這種策略適用於上述各類數據分佈方式。其優勢是很是簡單,其缺點是恢復數據的效率不高、可擴展性也不高。

更合適的作法不是以機器做爲副本單位,而是將數據拆爲較合理的數據段,以數據段爲單位做爲副本。

實踐中,經常使得每一個數據段的大小盡可能相等且控制在必定的大小之內。數據段有不少不一樣的稱謂,segment,fragment,chunk,partition 等等。數據段的選擇與數據分佈方式直接相關。

對於哈希分數據的方式,每一個哈希分桶後的餘數能夠做爲一個數據段,爲了控制數據段的大小,經常使得分桶個數大於集羣規模。一旦將數據分爲數據段,則能夠以數據段爲單位管理副本,從而副本與機器再也不硬相關,每臺機器均可以負責必定數據段的副本。

sv4rY4.md.jpg

一旦副本分佈與機器無關,數據丟失後的恢復效率將很是高。這是由於,一旦某臺機器的數據丟失,其上數據段的副本將分佈在整個集羣的全部機器中,而不是僅在幾個副本機器中,從而能夠從整個集羣同時拷貝恢復數據,而集羣中每臺數據源機器均可以以很是低的資源作拷貝。做爲恢復數據源的機器即便都限速1MB/s,如有100 臺機器參與恢復,恢復速度也能達到100MB/s。

再者,副本分佈與機器無關也利於集羣容錯。若是出現機器宕機,因爲宕機機器上的副本分散於整個集羣,其壓力也天然分散到整個集羣。

最後,副本分佈與機器無關也利於集羣擴展。理論上,設集羣規模 爲N 臺機器,當加入一臺新的機器時,只需從各臺機器上遷移1/N – 1/N+1 比例的數據段到新機器即實現了新的負載均衡。因爲是從集羣中各機器遷移數據,與數據恢復同理,效率也較高。

工程中,徹底按照數據段創建副本會引發須要管理的元數據的開銷增大,副本維護的難度也相應增大。一種折中的作法是將某些數據段組成一個數據段分組,按數據段分組爲粒度進行副本管理。這樣作能夠將副本粒度控制在一個較爲合適的範圍內。

本地化計算
在分佈式系統中,數據的分佈方式也深深影響着計算的分佈方式。在分佈式系統中計算節點和保存計算數據的存儲節點能夠在同一臺物理機器上,也能夠位於不一樣的物理機器。
若是計算節點和存儲節點位於不一樣的物理機器則計算的數據須要經過網絡傳輸,此種方式的開銷很大,甚至網絡帶寬會成爲系統的整體瓶頸。

另外一種思路是,將計算儘可能調度到與存儲節點在同一臺物理機器上的計算節點上進行,這稱之爲本地化計算。本地化計算是計算調度的一種重要優化,其體現了一種重要的分佈式調度思想:「移動數據不如移動計算」。

數據分佈方式的選擇
在實際工程實踐中,能夠根據需求及實施複雜度合理選擇數據分佈方式。另外,數據分佈方式是能夠靈活組合使用的,每每能夠兼備各類方式的優勢,收到較好的綜合效果。

例:數據傾斜問題,在按哈希分數據的基礎上引入按數據量分佈數據的方式,解決該數據傾斜問題。按用戶id 的哈希值分數據,當某個用戶id 的數據量特別大時,該用戶的數據始終落在某一臺機器上。此時,引入按數據量分佈數據的方式,統計用戶的數據量,並按某一閾值將用戶的數據切爲多個均勻的數據段,將這些數據段分佈到集羣中去。因爲大部分用戶的數據量不會超過閾值,因此元數據中僅僅保存超過閾值的用戶的數據段分佈信息,從而能夠控制元數據的規模。這種哈希分佈數據方式與按數據量分佈數據方式組合使用的方案,在某真實系統中使用,取得了較好的效果。

2.2 基本副本協議

副本控制協議指按特定的協議流程控制副本數據的讀寫行爲,使得副本知足必定的可用性和一致性要求的分佈式協議。副本控制協議要具備必定的對抗異常狀態的容錯能力,從而使得系統具備必定的可用性,同時副本控制協議要能提供必定一致性級別。由CAP 原理(在2.9 節詳細分析)可知,要設計一種知足強一致性,且在出現任何網絡異常時均可用的副本協議是不可能的。爲此,實際中的副本控制協議老是在可用性、一致性與性能等各要素之間按照具體需求折中。

副本控制協議能夠分爲兩大類:「中心化(centralized)副本控制協議」和「去中心化(decentralized)副本控制協議」。

中心化副本控制協議
中心化副本控制協議的基本思路是由一箇中心節點協調副本數據的更新、維護副本之間的一致性。

圖給出了中心化副本協議的通用架構。中心化副本控制協議的優勢是協議相對較爲簡單,全部的副本相關的控制交由中心節點完成。併發控制將由中心節點完成,從而使得一個分佈式併發控制問題,簡化爲一個單機併發控制問題。

所謂併發控制,即多個節點同時須要修改副本數據時,須要解決「寫寫」、「讀寫」等併發衝突。單機系統上經常使用加鎖等方式進行併發控制。對於分佈式併發控制,加鎖也是一個經常使用的方法,但若是沒有中心節點統一進行鎖管理,就須要徹底分佈式化的鎖系統,會使得協議很是複雜。

中心化副本控制協議的缺點是系統的可用性依賴於中心化節點,當中心節點異常或與中心節點通訊中斷時,系統將失去某些服務(一般至少失去更新服務),因此中心化副本控制協議的缺點正是存在必定的停服務時間。

sv47pd.md.jpg

primary-secondary 協議
在primary-secondary 類型的協議中,副本被分爲兩大類,其中有且僅有一個副本做爲primary 副本,除primary 之外的副本都做爲secondary 副本。維護primary 副本的節點做爲中心節點,中心節點負責維護數據的更新、併發控制、協調副本的一致性。

Primary-secondary 類型的協議通常要解決四大類問題:數據更新流程、數據讀取方式、Primary 副本的肯定和切換、數據同步(reconcile)。

數據更新基本流程

  • 1.數據更新都由primary 節點協調完成。

  • 2.外部節點將更新操做發給primary 節點

  • 3.primary 節點進行併發控制即肯定併發更新操做的前後順序

  • 4.primary 節點將更新操做發送給secondary 節點

  • 5.primary 根據secondary 節點的完成狀況決定更新是否成功並將結果返回外部節點
    sv4OnP.md.png
    在工程實踐中,若是由primary 直接同時發送給其餘N 個副本發送數據,則每一個 secondary 的更新吞吐受限於primary 總的出口網絡帶寬,最大爲primary 網絡出口帶寬的1/N。

    爲了解決這個問題,有些系統(例如,GFS),使用接力的方式同步數據,即primary 將更新發送給第一 個secondary 副本,第一個secondary 副本發送給第二secondary 副本,依次類推。

數據讀取方式
數據讀取方式也與一致性高度相關。若是隻須要最終一致性,則讀取任何副本均可以知足需求。

若是須要會話一致性,則能夠爲副本設置版本號,每次更新後遞增版本號,用戶讀取副本時驗證版本號,從而保證用戶讀到的數據在會話範圍內單調遞增。使用primary-secondary 比較困難的是實現強一致性。

因爲數據的更新流程都是由primary 控制的,primary 副本上的數據必定是最新的,因此 若是始終只讀primary 副本的數據,能夠實現強一致性。若是隻讀primary 副本,則secondary 副本將不提供讀服務。

實踐中,若是副本不與機器綁定,而是按照數據段爲單位維護副本,僅有primary 副本提供讀服務在不少場景下並不會造出機器資源浪費。

將副本分散到集羣中個,假設primary 也是隨機的肯定的,那麼每臺機器上都有一些數據的primary 副本,也有另外一些數據段的secondary 副本。從而某臺服務器實際都提供讀寫服務。

由primary 控制節點secondary 節點的可用性。當primary 更新某個secondary 副本不成功時,primary 將該secondary 副本標記爲不可用,從而用戶再也不讀取該不可用的副本。不可用的 secondary 副本能夠繼續嘗試與primary 同步數據,當與primary 完成數據同步後,primary 能夠副本標記爲可用。

這種方式使得全部的可用的副本,不管是primary 仍是secondary 都是可讀的,且在一個肯定的時間內,某secondary 副本要麼更新到與primary 一致的最新狀態,要麼被標記爲不可用,從而符合較高的一致性要求。這種方式依賴於一箇中心元數據管理系統,用於記錄哪些副本可用,哪些副本不可用。某種意義上,該方式經過下降系統的可用性來提升系統的一致性。

primary 副本的肯定與切換
在primary-secondary 類型的協議中,另外一個核心的問題是如何肯定primary 副本,尤爲是在原primary 副本所在機器出現宕機等異常時,須要有某種機制切換primary 副本,使得某個secondary 副本成爲新的primary 副本。

一般的,在primary-secondary 類型的分佈式系統中,哪一個副本是primary 這一信息都屬於元信息,由專門的元數據服務器維護。執行更新操做時,首先查詢元數據服務器獲取副本的primary 信息,從而進一步執行數據更新流程。

因爲分佈式系統中可靠的發現節點異常是須要必定的探測時間的,這樣的探測時間一般是10 秒級別,這也意味着一旦primary 異常,最多須要10 秒級別的發現時間,系統才能開始primary 的切換,在這10 秒時間內,因爲沒有primary,系統不能提供更 新服務,若是系統只能讀primary 副本,則這段時間內甚至不能提供讀服務。從這裏能夠看到,primary-backup 類副本協議的最大缺點就是因爲primary 切換帶來的必定的停服務時間。

數據同步
不一致的secondary 副本須要與primary 進行同步(reconcile)。

一般不一致的形式有三種:

  • 1、因爲網絡分化等異常,secondary 上的數據落後於primary 上的數據。

  • 2、在某些協議下,secondary 上的數據有多是髒數據,須要被丟棄。所謂髒數據是因爲primary 副本沒有進行某一更新操做,而secondary 副本上反而進行的多餘的修改操做,從而形成secondary 副本數據錯誤。

  • 3、secondary 是一個新增長的副本,徹底沒有數據,須要從其餘副本上拷貝數據。

    對於第一種secondary 數據落後的狀況,常見的同步方式是回放primary 上的操做日誌(一般是redo 日誌),從而追上primary 的更新進度。

    對於髒數據的狀況,較好的作法是設計的分佈式協議不產生髒數據。若是協議必定有產生髒數據的可能,則也應該使得產生髒數據的機率降到很是低得狀況,從而一旦發生髒數據的狀況能夠簡單的直接丟棄有髒數據的副本,這樣至關於副本沒有數據。

    另外,也能夠設計一些基於undo 日誌的方式從而能夠刪除髒數據。若是secondary 副本徹底沒有數據,則常見的作法是直接拷貝primary 副本的數據,這種方法每每比回放日誌追更新進度的方法快不少。但拷貝數據時primary 副本須要可以繼續提供更新服務,這就要求primary 副本支持快照(snapshot)功能。即對某一刻的副本數據造成快照,而後拷貝快照,拷貝完成後使用回放日誌的方式追快照造成後的更新操做。

去中心化副本控制協議
去中心化副本控制協議沒有中心節點,協議中全部的節點都是徹底對等的,節點之間經過平等協商達到一致。從而去中心化協議沒有由於中心化節點異常而帶來的停服務等問題。

去中心化協議的最大的缺點是協議過程一般比較複雜。尤爲當去中心化協議須要實現強一致性時,協議流程變得複雜且不容易理解。因爲流程的複雜,去中心化協議的效率或者性能通常也較中心化協議低。一個不恰當的比方就是,中心化副本控制協議相似專制制度,系統效率高但高度依賴於中心節點,一旦中心節點異常,系統受到的影響較大;去中心化副本控制協議相似民主制度,節點集體協商,效率低下,但個別節點的異常不會對系統整體形成太大影響。
sv4ztg.md.jpg

2.3 Lease 機制

Lease 機制是最重要的分佈式協議,普遍應用於各類實際的分佈式系統中。

基於lease 的分佈式cache 系統
基本的問題背景以下:在一個分佈式系統中,有一箇中心服務器節點,中心服務器存儲、維護着一些數據,這些數據是系統的元數據。系統中其餘的節點經過訪問中心服務器節點讀取、修改其上的元數據。

因爲系統中各類操做都依賴於元數據,若是每次讀取元數據的操做都訪問中心服務器 節點,那麼中心服務器節點的性能成爲系統的瓶頸。爲此,設計一種元數據cache,在各個節點上 cache 元數據信息,從而減小對中心服務器節點的訪問,提升性能。

另外一方面,系統的正確運行嚴格依賴於元數據的正確,這就要求各個節點上cache 的數據始終與中心服務器上的數據一致,cache 中的數據不能是舊的髒數據。最後,設計的cache 系統要能最大可能的處理節點宕機、網絡中斷等異常,最大程度的提升系統的可用性。

爲此,利用lease 機制設計一套cache 系統,其基本原理爲以下。

中心服務器在向各節點發送數據時同時向節點頒發一個lease。每一個lease 具備一個有效期,和信用卡上的有效期相似,lease 上的 有效期一般是一個明確的時間點,例如12:00:10,一旦真實時間超過這個時間點,則lease 過時失效。這樣lease 的有效期與節點收到lease 的時間無關,節點可能收到lease 時該lease 就已通過期失效。這裏首先假設中心服務器與各節點的時鐘是同步的,在下節中討論時鐘不一樣步對lease 的影響。

中心服務器發出的lease 的含義爲:在lease 的有效期內,中心服務器保證不會修改對應數據的值。所以,節點收到數據和lease 後,將數據加入本地Cache,一旦對應的lease 超時,節點將對應的本地cache 數據刪除。中心服務器在修改數據時,首先阻塞全部新的讀請求,並等待以前爲該數據發出的全部lease 超時過時,而後修改數據的值。

基於lease 的cache,客戶端節點讀取元數據

  • 1.判斷元數據是否已經處於本地cache 且lease 處於有效期內1.1 是:直接返回cache 中的元數據1.2 否:向中心服務器節點請求讀取元數據信息1.2.1 服務器收到讀取請求後,返回元數據及一個對應的lease 1.2.2 客戶端是否成功收到服務器返回的數據 1.2.2.1 失敗或超時:退出流程,讀取失敗,可重試1.2.2.2 成功:將元數據與該元數據的lease 記錄到內存中,返回元數據

  • 2.基於lease 的cache,客戶端節點修改元數據流程2.1 節點向服務器發起修改元數據請求。2.2 服務器收到修改請求後,阻塞全部新的讀數據請求,即接收讀請求,但不返回數據。2.3 服務器等待全部與該元數據相關的lease 超時。2.4 服務器修改元數據並向客戶端節點返回修改爲功。

    上述機制能夠保證各個節點上的cache 與中心服務器上的中心始終一致。這是由於中心服務器節點在發送數據的同時授予了節點對應的lease,在lease 有效期內,服務器不會修改數據,從而客戶端節點能夠放心的在lease 有效期內cache 數據。

    上述lease 機制能夠容錯的關鍵是:服務器一旦 發出數據及lease,不管客戶端是否收到,也不管後續客戶端是否宕機,也不管後續網絡是否正常,服務器只要等待lease 超時,就能夠保證對應的客戶端節點不會再繼續cache 數據,從而能夠放心的修改數據而不會破壞cache 的一致性。

    上述基礎流程有一些性能和可用性上的問題,但能夠很容易就優化改性。優化點一:服務器在修改元數據時首先要阻塞全部新的讀請求,形成沒有讀服務。這是爲了防止發出新的lease 從而引發不斷有新客戶端節點持有lease 並緩存着數據,造成「活鎖」。優化的方法很簡單,服務器在進入修改數據流程後,一旦收到讀請求則只返回數據但不頒發lease。

    從而形成在修改流程執行的過程當中,客戶端能夠讀到元數據,只是不能緩存元數據。進一步的優化是,當進入修改流程,服務器頒發的lease 有效期限選擇爲已發出的lease 的最大有效期限。這樣作,客戶端能夠繼續在服務器進入修改流程後繼續緩存元數據,但服務器的等待全部lease 過時的時間也不會由於頒發新的lease 而不斷延長。

    最後,=cache 機制與多副本機制的區別。Cache 機制與多副本機制的類似之處都 是將一份數據保存在多個節點上。但Cache 機制卻要簡單許多,對於cache 的數據,能夠隨時刪除丟棄,並命中cache 的後果僅僅是須要訪問數據源讀取數據;然而副本機制卻不同,副本是不能隨意丟棄的,每失去一個副本,服務質量都在降低,一旦副本數降低到必定程度,則每每服務將再也不可用。

lease 機制的分析
lease 的定義:Lease 是由頒發者授予的在某一有效期內的承諾。頒發者一旦發出lease,則不管接受方是否收到,也不管後續接收方處於何種狀態,只要lease 不過時,頒發者必定嚴守承諾;另外一方面,接收方在lease 的有效期內可使用頒發者的承諾,但一旦lease 過時,接收方必定不能繼續使用頒發者的承諾。

Lease 機制具備很高的容錯能力。首先,經過引入有效期,Lease 機制可否很是好的容錯網絡異常。Lease 頒發過程只依賴於網絡能夠單向通訊,即便接收方沒法向頒發者發送消息,也不影響lease 的頒發。

因爲lease 的有效期是一個肯定的時間點,lease 的語義與發送lease 的具體時間無關,因此 同一個lease 能夠被頒發者不斷重複向接受方發送。即便頒發者偶爾發送lease 失敗,頒發者也能夠 簡單的經過重發的辦法解決。一旦lease 被接收方成功接受,後續lease 機制再也不依賴於網絡通訊,即便網絡徹底中斷lease 機制也不受影響。

再者,Lease 機制能較好的容錯節點宕機。若是頒發者宕機,則宕機的頒發者一般沒法改變以前的承諾,不會影響lease 的正確性。在頒發者機恢復後,若是頒發者恢復出了以前的lease 信息,頒發者能夠繼續遵照lease 的承諾。若是頒發者沒法恢復lease 信息,則只需等待一個最大的lease 超時時間就可使得全部的lease 都失效,從而不破壞lease機制。

例如上節中的cache 系統的例子中,一旦服務器宕機,確定不會修改元數據,從新恢復後,只需等待一個最大的lease 超時時間,全部節點上的緩存信息都將被清空。

對於接受方宕機的狀況,頒發者 不須要作更多的容錯處理,只需等待lease 過時失效,就能夠收回承諾,實踐中也就是收回以前賦予的權限、身份等。最後,lease 機制不依賴於存儲。頒發者能夠持久化頒發過的lease 信息,從而在 宕機恢復後可使得在有效期的lease 繼續有效。但這對於lease 機制只是一個優化,如以前的分析,即便頒發者沒有持久化lease 信息,也能夠經過等待一個最大的lease 時間的方式使得以前全部頒發 的lease 失效,從而保證機制繼續有效。

Lease 機制依賴於有效期,這就要求頒發者和接收者的時鐘是同步的。一方面,若是頒發者的 時鐘比接收者的時鐘慢,則當接收者認爲lease 已通過期的時候,頒發者依舊認爲lease 有效。接收者能夠用在lease 到期前申請新的lease 的方式解決這個問題。另外一方面,若是頒發者的時鐘比接收 者的時鐘快,則當頒發者認爲lease 已通過期的時候,接收者依舊認爲lease 有效,頒發者可能將lease 頒發給其餘節點,形成承諾失效,影響系統的正確性。對於這種時鐘不一樣步,實踐中的一般作法是將頒發者的有效期設置得比接收者的略大,只需大過期鍾偏差就能夠避免對lease 的有效性的影響。

基於lease 機制肯定節點狀態
分佈式協議依賴於對節點狀態認知的全局一致性,即一旦節點Q 認爲某個節點 A 異常,則節點A 也必須認爲本身異常,從而節點A 中止做爲primary,避免「雙主」問題的出現。

解決這種問題有兩種思路,第1、設計的分佈式協議能夠容忍「雙主」錯誤,即不依賴於對節點狀 態的全局一致性認識,或者全局一致性狀態是全體協商後的結果;

第2、利用lease 機制。對於第一 種思路即放棄使用中心化的設計,而改用去中心化設計,超過本節的討論範疇。下面着重討論利用 lease 機制肯定節點狀態。

由中心節點向其餘節點發送lease,若某個節點持有有效的lease,則認爲該節點正常能夠提供服 務。用於例2.3.1 中,節點A、B、C 依然週期性的發送heart beat 報告自身狀態,節點Q 收到heart beat 後發送一個lease,表示節點Q 確認了節點A、B、C 的狀態,並容許節點在lease 有效期內正常工 做。節點Q 能夠給primary 節點一個特殊的lease,表示節點能夠做爲primary 工做。一旦節點Q 但願切換新的primary,則只需等前一個primary 的lease 過時,則就能夠安全的頒發新的lease 給新的 primary 節點,而不會出現「雙主」問題。

在實際系統中,若用一箇中心節點發送lease 也有很大的風險,一旦該中心節點宕機或網絡異常,則全部的節點沒有lease,從而形成系統高度不可用。爲此,實際系統老是使用多箇中心節點互爲副本,成爲一個小的集羣,該小集羣具備高可用性,對外提供頒發lease 的功能。chubby 和zookeeper 都是基於這樣的設計。

lease 的有效期時間選擇
工程中,常選擇的lease 時長是10 秒級別,這是一個通過驗證的經驗值,實踐中能夠做爲參考並綜合選擇合適的時長。

2.4 Quorum 機制

先作這樣的約定:更新操做(write)是一系列順序的過程,經過其餘機制肯定更新操做的順序(例如primary-secondary 架構中由primary 決定順序),每一個更新操做記爲wi, i 爲更新操做單調遞增的序號,每一個wi 執行成功後副本數據都發生變化,稱爲不一樣的數據版本,記 做vi。假設每一個副本都保存了歷史上全部版本的數據。

write-all-read-one
Write-all-read-one(簡稱WARO)是一種最簡單的副本控制規則,顧名思義即在更新時寫全部的副本,只有在全部的副本上更新成功,才認爲更新成功,從而保證全部的副本一致,這樣在讀取數據時能夠讀任一副本上的數據。

因爲更新操做須要在全部的N 個副本上都成功,更新操做才能成 功,因此一旦有一個副本異常,更新操做失敗,更新服務不可用。對於更新服務,雖然有N 個副本, 但系統沒法容忍任何一個副本異常。另外一方面,N 個副本中只要有一個副本正常,系統就能夠提供讀服務。對於讀服務而言,當有N 個副本時,系統能夠容忍N-1 個副本異常。從上述分析能夠發現WARO 讀服務的可用性較高,但更新服務的可用性不高,甚至雖然使用了副本,但更新服務的可用性等效於沒有副本。

Quorum 定義
在Quorum 機制下,當某次更新操做wi 一旦在全部N 個副本中的W 個副本上都成功,則就稱 該更新操做爲「成功提交的更新操做」,稱對應的數據爲「成功提交的數據」。令R>N-W,因爲更新 操做wi 僅在W 個副本上成功,因此在讀取數據時,最多須要讀取R 個副本則必定能讀到wi 更新後 的數據vi 。若是某次更新wi 在W 個副本上成功,因爲W+R>N,任意R 個副本組成的集合必定與 成功的W個副本組成的集合有交集,因此讀取R 個副本必定能讀到wi 更新後的數據vi。

如圖 2-10, Quorum 機制的原理能夠文森圖表示。
sv5kn0.md.png

某系統有5 個副本,W=3,R=3,最初5 個副本的數據一致,都是v1,某次更新操做 w2 在前3 副本上成功,副本狀況變成(v2 v2 v2 v1 v1)。

此時,任意3 個副本組成的集合中必定包括 v2。在上述定義中,令W=N,R=1,就獲得WARO,即WARO 是Quorum 機制的一種特例。與分析WARO 類似,分析Quorum 機制的可用性。限制Quorum 參數爲W+R=N+1。因爲更新 操做須要在W 個副本上都成功,更新操做才能成功,因此一旦N-W+1 個副本異常,更新操做始終沒法在W 個副本上成功,更新服務不可用。

另外一方面,一旦N-R+1 個副本異常,則沒法保證必定能夠讀到與W 個副本有交集的副本集合,則讀服務的一致性降低。

再次強調:僅僅依賴quorum 機制是沒法保證強一致性的。由於僅有quorum 機制時沒法肯定最新已成功提交的版本號,除非將最新已提交的版本號做爲元數據由特定的元數據服務器或元數據集羣管理,不然很難肯定最新成功提交的版本號。在下一節中,將討論在哪些狀況下,能夠僅僅 經過quorum 機制來肯定最新成功提交的版本號。

Quorum 機制的三個系統參數N、W、R 控制了系統的可用性,也是系統對用戶的服務承諾:數據最多有N 個副本,但數據更新成功W 個副本即返回用戶成功。對於一致性要求較高的Quorum 系統,系統還應該承諾任什麼時候候不讀取未成功提交的數據,即讀取到的數據都是曾經在W 個副本上成功的數據。

讀取最新成功提交的數據
Quorum 機制只需成功更新N 個副本中的W 個,在讀取R 個副本時,必定能夠讀到最新的成功提交的數據。但因爲有不成功的更新狀況存在,僅僅讀取R 個副本卻不必定能肯定哪一個版本的數據 是最新的已提交的數據。對於一個強一致性Quorum 系統,若存在個數據少於W 個,假設爲X 個,則繼續讀取其餘副本,直若成功讀取到W 個 該版本的副本,則該數據爲最新的成功提交的數據;若是在全部副本中該數據的個數確定不滿 足W 個,則R 中版本號第二大的爲最新的成功提交的副本。

例:在讀取到(v2 v1 v1)時,繼續讀取剩餘的副本,若讀到剩餘兩個副本 爲(v2 v2)則v2 是最新的已提交的副本;若讀到剩餘的兩個副本爲(v2 v1)或(v1 v1)則v1 是最新成功提交的版本;若讀取後續兩個副本有任一超時或失敗,則沒法判斷哪一個版本是最新的成功提交的版本。

能夠看出,在單純使用Quorum 機制時,若要肯定最新的成功提交的版本,最多須要讀取R+ (W-R-1)=N 個副本,當出現任一副本異常時,讀最新的成功提交的版本這一功能都有可能不可用。

實際工程中,應該儘可能經過其餘技術手段,迴避經過Quorum 機制讀取最新的成功提交的版本。例如,當quorum 機制與primary-secondary 控制協議結合使用時,能夠經過讀取primary 的方式讀取到最新的已提交的數據。

基於Quorum 機制選擇primary副本
讀取數據時依照一致性要求的不一樣能夠有不一樣的作法:若是須要強一致性的馬上讀取到最新的成功提交的數據,則能夠簡單的只讀取primary 副本上的數據便可,也能夠經過上節的方式讀取;

若是須要會話一致性,則能夠根據以前已經讀到的數據版本號在各個副本上進行選擇性讀取;若是隻須要弱一致性,則能夠選擇任意副本讀取。

在primary-secondary 協議中,當primary 異常時,須要選擇出一個新的primary,以後secondary 副本與primary 同步數據。

一般狀況下,選擇新的primary 的工做是由某一中心節點完成的,在引入 quorum 機制後,經常使用的primary 選擇方式與讀取數據的方式相似,即中心節點讀取R 個副本,選擇 R 個副本中版本號最高的副本做爲新的primary。新primary 與至少W 個副本完成數據同步後做爲新的primary 提供讀寫服務。

首先,R 個副本中版本號最高的副本必定蘊含了最新的成功提交的數據。再者,雖然不能肯定最高版本號的數是一個成功提交的數據,但新的primary 在隨後與secondary 同 步數據,使得該版本的副本個數達到W,從而使得該版本的數據成爲成功提交的數據。

例:在N=5,W=3,R=3 的系統中,某時刻副本最大版本號爲(v2 v2 v1 v1 v1),此時v1 是系統的最新的成功提交的數據,v2 是一個處於中間狀態的未成功提交的數據。假設此刻原primary 副本異常,中心節點進行primary 切換工做。這類「中間態」數據究竟做爲「髒數據」被刪除,仍是做爲新的數據被同步後成爲生效的數據,徹底取決於這個數據可否參與新primary 的選舉。下面分別分析這兩種狀況。
sv5ZAU.md.jpg

第1、如圖 2-12,若中心節點與其中3 個副本通訊成功,讀取到的版本號爲(v1 v1 v1),則任 選一個副本做爲primary,新primary 以v1 做爲最新的成功提交的版本並與其餘副本同步,當與第一、第2 個副本同步數據時,因爲第一、第2 個副本版本號大於primary,屬於髒數據,能夠按照2.2.2.4 節中介紹的處理髒數據的方式解決。

實踐中,新primary 也有可能與後兩個副本完成同步後就提供數據服務,隨後自身版本號也更新到v2,若是系統不能保證以後的v2 與以前的v2 徹底同樣,則新 primary 在與第一、2 個副本同步數據時不但要比較數據版本號還須要比較更新操做的具體內容是否同樣。

sv5E7T.md.jpg

第2、若中心節點與其餘3 個副本通訊成功,讀取到的版本號爲(v2 v1 v1),則選取版本號爲 v2 的副本做爲新的primary,以後,一旦新primary 與其餘2 個副本完成數據同步,則符合v2 的副 本個數達到W 個,成爲最新的成功提交的副本,新primary 能夠提供正常的讀寫服務。

2.5 日誌技術

日誌技術是宕機恢復的主要技術之一。日誌技術最初使用在數據庫系統中。嚴格來講日誌技術不是一種分佈式系統的技術,但在分佈式系統的實踐中,卻普遍使用了日誌技術作宕機恢復,甚 至如BigTable 等系統將日誌保存到一個分佈式系統中進一步加強了系統容錯能力。

Redo Log 與Check point
設計一個高速的單機查詢系統,將數據所有存放在內存中以實現高速的數據查詢,每次更新操做更新一小部分數據(例如 key-value 中的某一個key)。如今問題爲利用日誌技術實現該內存查詢系統的宕機恢復。與數據庫的事務不一樣的是,這個問題模型中的每一個成功的更新操做都會生效。這也等效爲數據庫的每一個事務只有一個更新操做,且每次更新操做均可以也必須當即提交(Auto commit)。
Redo Log
-1.將更新操做的結果(例如Set K1=1,則記錄K1=1)以追加寫(append)的方式寫入磁盤的 日誌文件

  • 2.按更新操做修改內存中的數據

  • 3.返回更新成功

    從Redo Log 的流程能夠看出,Redo 寫入日誌的是更新操做完成後的結果(雖然本文不討論Undo Log,這點是與Undo Log 的區別之一),且因爲是順序追加寫日誌文件,在磁盤等對順序寫有力的 存儲設備上效率較高。
    用Redo Log 進行宕機恢復很是簡單,只須要「回放」日誌便可。

    從Redo Log 的宕機恢復流程也能夠看出,只有寫入日誌文件的更新結果才能在宕機後恢復。這也是爲何在Redo Log 流程中須要先更新日誌文件再更新內存中的數據的緣由。

    假如先更新內存中的數據,那麼用戶馬上就能讀到更新後的數據,一旦在完成內存修改與寫入日誌之間發生宕機,那麼最後一次更新操做沒法恢復,但以前用戶可能已經讀取到了更新後的數據,從而引發不一致的問題。

    在簡化的模型下,check point 技術的過程即將內存中的數據以某種易於從新加載的數據組織方式完整的dump 到磁盤,從而減小宕機恢復時須要回放的日誌數據。

流程:check point

  • 1.向日志文件中記錄「Begin Check Point」
  • 2.將內存中的數據以某種易於從新加載的數據組織方式dump 到磁盤上
  • 3.向日志文件中記錄「End Check Point」 在check point 流程中,數據能夠繼續按照流程2.5.1 被更新,這段過程當中新更新的數據能夠dump 到磁盤也能夠不dump 到磁盤,具體取決於實現。例如,check point 開始時k1=v1,check point 過程 中某次更新爲k1 = v2,那麼dump 到磁盤上的k1 的值能夠是v1 也能夠是v2。

流程:基於check point 的宕機恢復流程

  • 1.將dump 到磁盤的數據加載到內存。

  • 2.從後向前掃描日誌文件,尋找最後一個「End Check Point」日誌。

  • 3.從最後一個「End Check Point」日誌向前找到最近的一個「Begin Check Point」日誌,並回 放該日誌以後的全部更新操做日誌。

    若數據維護在磁盤中,某批更新由若干個更新操做組成,這些更新操做須要原子生效,即要麼同時生效,要麼都不生效。
    sv5McR.md.jpg
    0/1 目錄技術中有兩個目錄結構,稱爲目錄0(Directory 0)和目錄1(Directory 1)。另有一個結構稱爲主記錄(Master record)記錄當前正在使用的目錄稱爲活動目錄。主記錄中要麼記錄使用目錄0,要麼記錄使用目錄1。目錄0 或目錄1 中記錄了各個數據的在日誌文件中的位置。0/1 目錄的數據更新過程始終在非活動目錄上進行,只是在數據生效前,將主記錄中的0、1 值反轉,從而切換主記錄。

流程:0/1 目錄數據更新流程

  • 1.將活動目錄完整拷貝到非活動目錄。

  • 2.對於每一個更新操做,新建一個日誌項紀錄操做後的值,並在非活動目錄中將相應數據的位置修改成新建的日誌項的位置。

  • 3.原子性修改主記錄:反轉主記錄中的值,使得非活動目錄生效。

    0/1 目錄的更新流程很是簡單,經過0、1 目錄的主記錄切換使得一批修改的生效是原子的。0/1 目錄將批量事務操做的原子性經過目錄手段歸結到主記錄的原子切換。

    因爲多條記錄的原子修改通常較難實現而單條記錄的原子修改每每能夠實現,從而下降了問題實現的難度。

    在工程中0/1 目錄的思想運用很是普遍,其形式也不侷限在上述流程中,能夠是內存中的兩個數據結構來回切換,也能夠是磁盤上的兩個文件目錄來回生效切換。

2.6 兩階段提交協議

兩階段提交協議是一種經典的強一致性中心化副本控制協議。雖然在工程中該協議有較多的問題,但研究該協議能很好的理解分佈式系統的幾個典型問題。

流程描述
兩階段提交協議是一種典型的「中心化副本控制」協議。在該協議中,參與的節點分爲兩類:一箇中心化協調者節點(coordinator)和N 個參與者節點(participant)。每一個參與者節點即上文背景介紹中的管理數據庫副本的節點。

兩階段提交的思路比較簡單,在第一階段,協調者詢問全部的參與者是否能夠提交事務(請參與者投票),全部參與者向協調者投票。

在第二階段,協調者根據全部參與者的投票結果作出是否事務能夠全局提交的決定,並通知全部的參與者執行該決定。在一個兩階段提交流程中,參與者不能改變本身的投票結果。

兩階段提交協議的能夠全局提交的前提是全部的參與者都贊成提交事務,只要有一個參與者投票選擇放棄(abort)事務,則事務必須被放棄。

流程:兩階段提交協調者流程

  • 1.寫本地日誌「begin_commit」,並進入WAIT 狀態;

  • 2.向全部參與者發送「prepare 消息」;

  • 3.等待並接收參與者發送的對「prepare 消息」的響應;3.1 若收到任何一個參與者發送的「vote-abort 消息」;3.1.1 寫本地「global-abort」日誌,進入ABORT;3.1.2 向全部的參與者發送「global-abort 消息」;3.1.3 進入ABORT 狀態;3.2 若收到全部參與者發送的「vote-commit」消息;3.2.1 寫本地「global-commit」日誌,進入COMMIT 狀態;3.1.2 向全部的參與者發送「global-commit 消息」;

  • 4.等待並接收參與者發送的對「global-abort 消息」或「global-commit 消息」的確認響應消息,一旦收到全部參與者的確認消息,寫本地「end_transaction」 日誌流程結束。

流程:兩階段提交協調者流程

  • 1.寫本地日誌「init」記錄,進入INIT 狀態

  • 2.等待並接受協調者發送的「prepare 消息」,收到後 2.1 若參與者能夠提交本次事務 2.1.1 寫本地日誌「ready」,進入READY 狀態 2.1.2 向協調者發送「vote-commit」消息 2.1.4 等待協調者的消息2.1.4.1 若收到協調者的「global-abort」消息2.1.4.1.1 寫本地日誌「abort」,進入ABORT 狀態2.1.4.1.2 向協調者發送對「global-abort」的確認消息 2.1.4.2 若收到協調者的「global-commit」消息2.1.4.1.1 寫本地日誌「commit」,進入COMMIT 狀態 2.1.4.1.2 向協調者發送對「global-commit」的確認消息 2.2 若參與者沒法提交本次事務 2.2.1 寫本地日誌「abort」,進入ABORT 狀態 2.2.2 向協調者發送「vote-abort」消息 2.2.3 流程對該參與者結束 2.2.4 若後續收到協調者的「global-abort」消息能夠響應

  • 3.即便流程結束,但任什麼時候候收到協調者發送的「global-abort」消息或「global-commit」消息也都要發送一個對應的確認消息。

異常處理
宕機恢復

  • 1.協調者宕機恢復 協調者宕機恢復後,首先經過日誌查找到宕機前的狀態。若是日誌中最後是「begin_commit」記錄,說明宕機前協調者處於WAIT 狀態,協調者可能已經發送過「prepare 消息」也可能還沒發送,但協調者必定尚未發送過「global-commit 消息」或「global-abort 消息」,即事務的全局狀態尚未肯定。此時,協調者能夠從新發送「prepare 消息」 繼續兩階段提交流程,即便參與者已經發送過對「prepare 消息」的響應,也不過是再次重傳以前的響應而不會影響協議的一致性。若是日誌中最後是「global-commit」或「global-abort」記錄,說明宕機前協調者處於COMMIT 或ABORT 狀態。此時協調者只需從新向全部的參與者發送「global-commit 消息」或「global-abort 消息」就能夠繼續兩階段提交流程。

  • 2.參與者宕機恢復參與者宕機恢復後,首先經過日誌查找宕機前的狀態。若是日誌中最後是「init」記錄,說明參與者處於INIT 狀態,尚未對本次事務作出投票選擇,參與者能夠繼續流程等待協調者發送的「prepare 消息」。若是日誌中最後是「ready」記錄,說明參與者處於REDAY 狀態,此時說明參與者已經就本次 事務作出了投票選擇,但宕機前參與者是否已經向協調者發送「vote-commit」消息並不可知。因此此時參與者能夠向協調者重發「vote-commit」,並繼續協議流程。若是日誌中最後是「commit」或「abort」記錄,說明參與者已經收到過協調者的「global-commit 消息」(處於COMMIT 狀態)或者「global-abort 消息」(處於ABORT 狀態)。至因而否向協調者發 送過對「global-commit」或「global-abort」的確認消息則未知。但即便沒有發送過確認消息,因爲協調者會不斷重發「global-commit」或「global-abort」,只需在收到這些消息時發送確認消息既可,不影響協議的全局一致性。

協議分析
兩階段提交協議在工程實踐中真正使用的較少,主要緣由有如下幾點:

  • 1.兩階段提交協議的容錯能力較差。從上文的分析能夠看出,兩階段提交協議在某些狀況下存在流程沒法執行下去的狀況,且也沒法判斷流程狀態。在工程中好的分佈式協議每每老是能夠在即便發生異常的狀況下也能執行下去。例如,回憶Lease 機制(2.3 ),一旦lease 發出,不管出現任何異常,Lease 服務器節點老是能夠經過時間斷定出Lease 是否有效,也能夠用等待Lease 超時的方法收回Lease 權限,整個Lease 協議的流程不存在任何流程被阻塞而沒法執行下去的狀況。與Lease 機制的簡單有效相比,兩階段提交的協議顯得較爲複雜且容錯能力差。

  • 2.兩階段提交協議的性能較差。一次成功的兩階段提交協議流程中,協調者與每一個參與者 之間至少須要兩輪交互4 個消息「prepare」、「vote-commit」、「global-commit」、「確認global-commit」。過多的交互次數會下降性能。另外一方面,協調者須要等待全部的參與者的投票結果,一旦存在較慢的參與者,會影響全局流程執行速度。

    雖然存在一些改進的兩階段提交協議能夠提升容錯能力和性能,然而這類協議依舊是在工程中使用較少的一類協議,其理論價值大於實踐意義。

2.7 MVCC

MVCC(Multi-version Cocurrent Control,多版本併發控制)技術。MVCC 技術最初也是在數據庫系統中被提出,但這種思想並不侷限於單機的分佈式系統,在分佈式系統中一樣有效。

MVCC 即多個不一樣版本的數據實現併發控制的技術,其基本思想是爲每次事務生成 一個新版本的數據,在讀數據時選擇不一樣版本的數據便可以實現對事務結果的完整性讀取。在使用MVCC 時,每一個事務都是基於一個已生效的基礎版本進行更新,事務能夠並行進行,從而能夠產生一種圖狀結構。
sv5dgA.md.jpg
基礎數據的版本爲1,同時產生了兩個事務:事務A 與事務B。這兩個事務都各自對數據進行了一些本地修改(這些修改只有事務本身可見,不影響真正的數據),以後事務A 首先提交,生成數據版本2;基於數據版本2,又發起了事務C,事務C 繼續提交,生成了數據版 本3;最後事務B 提交,此時事務B 的結果須要與事務C 的結果合併,若是數據沒有衝突,即事務 B 沒有修改事務A 與事務C 修改過的變量,那麼事務B 能夠提交,不然事務B 提交失敗。

MVCC 的流程過程很是相似於SVN 等版本控制系統的流程,或者說SVN 等版本控制系統就是 使用的MVCC 思想。事務在基於基礎數據版本作本地修改時,爲了避免影響真正的數據,一般有兩種作法,一是將基礎數據版本中的數據徹底拷貝出來再修改,SVN 即便用了這種方法,SVN check out 便是拷貝的過程;二是每一個事務中只記錄更新操做,而不記錄完整的數據,讀取數據時再將更新操做應用到用基礎版本的數據從而計算出結果,這個過程也相似SVN 的增量提交。

2.8 Paxos協議

Paxos 協議是少數在工程實踐中證明的強一致性、高可用的去中心化分佈式協議。Paxos 協議的流程較爲複雜,但其基本思想卻不難理解,相似於人類社會的投票過程。Paxos 協議中,有一組徹底對等的參與節點(稱爲accpetor),這組節點各自就某一事件作出決議,若是某個決議得到了超過半數節點的贊成則生效。Paxos 協議中只要有超過一半的節點正常,就能夠工做,能很好對抗宕機、網絡分化等異常狀況。

角色
Proposer:提案者。Proposer 能夠有多個,Proposer 提出議案(value)。所謂value,在工程中能夠是任何操做,例如「修改某個變量的值爲某個值」、「設置當前primary 爲某個節點」等等。Paxos 協議中統一將這些操做抽象爲value。不一樣的Proposer 能夠提出不一樣的甚至矛盾的value,例如某個Proposer 提議「將變量X 設置爲1」,另外一個Proposer 提議「將變量X 設置爲2」,但對同一輪Paxos 過程,最多隻有一個value 被批准。Acceptor:批准者。Acceptor 有N 個,Proposer 提出的value 必須得到超過半數(N/2+1)的Acceptor 批准後才能經過。Acceptor 之間徹底對等獨立。Learner:學習者。Learner 學習被批准的value。所謂學習就是經過讀取各個Proposer 對value 的選擇結果,若是某個value 被超過半數Proposer 經過,則Learner 學習到了這個value。回憶(2.4 ) 不難理解,這裏相似Quorum 機制,某個value 須要得到W=N/2 + 1 的Acceptor 批准,從而學習者須要至少讀取N/2+1 個Accpetor,至多讀取N 個Acceptor 的結果後,能學習到一個經過的value。上述三類角色只是邏輯上的劃分,實踐中一個節點能夠同時充當這三類角色。

流程
Paxos 協議一輪一輪的進行,每輪都有一個編號。每輪Paxos 協議可能會批准一個value,也可 能沒法批准一個value。若是某一輪Paxos 協議批准了某個value,則之後各輪Paxos 只能批准這個 value。上述各輪協議流程組成了一個Paxos 協議實例,即一次Paxos 協議實例只能批准一個value,這也是Paxos 協議強一致性的重要體現。每輪Paxos 協議分爲階段,準備階段和批准階段,在這兩個階段Proposer 和Acceptor 有各自的處理流程。

流程:Proposer 的流程 (準備階段)

  • 1.向全部的Acceptor 發送消息「Prepare(b)」;這裏b 是Paxos 的輪數,每輪遞增

  • 2.若是收到任何一個Acceptor 發送的消息「Reject(B)」,則對於這個Proposer 而言本輪Paxos 失敗,將輪數b 設置爲B+1 後從新步驟1;(批准階段,根據收到的Acceptor 的消息做出不一樣選擇)

  • 3.若是接收到的Acceptor 的「Promise(b, v_i)」消息達到N/2+1 個(N 爲Acceptor 總數,除法取整, 下同);v_i 表示Acceptor 最近一次在i 輪批准過value v。3.1 若是收到的「Promise(b, v)」消息中,v 都爲空,Proposer 選擇一個value v,向全部Acceptor 廣播Accept(b, v);3.2 不然,在全部收到的「Promise(b, v_i)」消息中,選擇i 最大的value v,向全部Acceptor 廣播消息Accept(b,v);

  • 4.若是收到Nack(B),將輪數b 設置爲B+1 後從新步驟1;

流程:Accpetor 流程 (準備階段)

  • 1.接受某個Propeser 的消息Prepare(b)。參數B 是該Acceptor 收到的最大Paxos 輪數編號;V 是Acceptor 批准的value,能夠爲空 1.1 若是b>B,回覆Promise(b, V_B),設置B=b; 表示保證再也不接受編號小於b 的提案。1.2 不然,回覆Reject(B) (批准階段)

  • 2.接收Accept(b, v), 2.1 若是b < B, 回覆Nack(B),暗示proposer 有一個更大編號的提案被這個Acceptor 接收了 2.2 不然設置V=v。表示這個Acceptor 批准的Value 是v。廣播Accepted 消息。

例子
基本例子裏有5 個Acceptor,1 個Proposer,不存在任何網絡、宕機異常。咱們着重考察各個Accpetor 上變量B 和變量V 的變化,及Proposer 上變量b 的變化。

  • 1.初始狀態
    svInVf.md.jpg

  • 2.Proposer 向全部Accpetor 發送「Prepare(1)」,全部Acceptor 正確處理,並回復Promise(1, NULL
    svIeqP.md.jpg

  • 3.Proposer 收到5 個Promise(1, NULL),知足多餘半數的Promise 的value 爲空,此時發送 Accept(1, v1),其中v1 是Proposer 選擇的Value。
    svIZrt.md.jpg

  • 4.此時,v1 被超過半數的Acceptor 批准,v1 便是本次Paxos 協議實例批准的Value。若是Learner 學習value,學到的只能是v1

    在同一個Paxos 實例中,批准的Value 是沒法改變的,即便後續Proposer 以更高的序號發起Paxos 協議也沒法改變value。Paxos 協議的核心就在於「批准的value 沒法改變」,這也是整個協議正確性的基礎。

    Paxos 協議是被人爲設計出來,其設計過程也是協議的推導過程。Paxos 協議利用了Quorom 機 制,選擇的W=R=N/2+1。

    簡單而言,協議就是Proposer 更新Acceptor 的過程,一旦某個Acceptor 成功更新了超過半數的Acceptor,則更新成功。Learner 按Quorum 去讀取Acceptor,一旦某個value 在超過半數的Proposer 上被成功讀取,則說明這是一個被批准的value。協議經過引入輪次,使得高輪次的提議搶佔低輪次的提議來避免死鎖。協議設計關鍵點是如何知足「在一次Paxos 算法實例過程當中只批准一個Value」這一約束條件。

2.9 CAP

CAP 理論的定義很簡單,CAP 三個字母分別表明了分佈式系統中三個相互矛盾的屬性:

  • 1.Consistency (一致性):CAP 理論中的副本一致性特指強一致性(1.3.4 );

  • 2.Availiablity(可用性):指系統在出現異常時已經能夠提供服務;

  • 3.Tolerance to the partition of network (分區容忍):指系統能夠對網絡分區(1.1.4.2 )這種異常情 況進行容錯處理;

    CAP 理論指出:沒法設計一種分佈式協議,使得同時徹底具有CAP 三個屬性,即1)該種協議下的副本始終是強一致性,2)服務始終是可用的,3)協議能夠容忍任何網絡分區異常;分佈式系統協議只能在CAP 這三者間全部折中。

    熱力學第二定律說明了永動機是不可能存在的,不要去妄圖設計永動機。與之相似,CAP 理論的意義就在於明確提出了不要去妄圖設計一種對CAP 三大屬性都徹底擁有的完美系統,由於這種系統在理論上就已經被證實不存在。

  • Lease 機制: Lease 機制犧牲了部分異常狀況下的A,從而得到了徹底的C 與很好的P。

  • Quorum 機制: Quorum 機制,在CAP 三大因素中都各作了折中,有必定的C,有較好 的A,也有較好的P,是一種較爲平衡的分佈式協議。

  • 兩階段提交協議: 兩階段提交系統具備徹底的C,很糟糕的A,很糟糕的P。

  • Paxos 協議:一樣是強一致性協議,Paxos 在CAP 三方面較之兩階段提交協議要優秀得多。Paxos 協議具備 徹底的C,較好的A,較好的P。Paxos 的A 與P 的屬性與Quorum 機制相似,由於Paxos 的協議本 身就具備Quorum 機制的因素。

相關文章
相關標籤/搜索