倘若我說有三個節點(計算機)要維護同一分數據,若是你對分佈式系統並不瞭解,那麼你可能會有什麼問題呢,我想可能有兩個最基本的問題:html
第一個問題,爲何要同一分數據要保存多分,是由於分佈式系統中的節點都有必定的機率發生故障,雖然單個節點的故障機率比較小,但當系統規模不斷上升,故障的機率就變大了許多。節點的故障會對系統的可用性、可靠性產生影響。當數據在系統中只有一份存儲時,若是發生斷電、主機crash、網絡故障,那麼致使的是數據的暫時不可用;若是發生磁盤損壞,則會致使數據丟失,也就是系統不可靠。所以數據冗餘(複製集、副本集),即同一分數據在系統中不一樣節點保存多分,能夠有效提升系統的可用性和數據的可靠性。固然,數據冗餘也有一些缺點,好比佔用額外的帶寬和存儲資源。算法
第二個問題一下就命中了複製集的要害,也是分佈式系統中對複雜的問題之一: 副本的一致性問題,即從系統外部讀取系統內部各個副本間的數據在必定的約束條件下是一致的。在這篇文章中介紹過CAP理論,即對於分佈式數據存儲,最多隻能同時知足一致性(C,Consistency)、可用性(A, Availability)、分區容錯性(P,Partition Tolerance)中的二者。而分佈式系統中網絡分割必定是存在的,因此CAP -- 「it is really just A vs C!"mongodb
同時必須注意的是,A和C都是一個度的問題,而不是0和1兩個極端,所以能夠在知足必定的可用性同時保證必定的一致性。不一樣的需求決定了究竟是一致性重要仍是可用性更重要,好比在購物網站中,可用性很是重要,分分鐘的不可訪問都會帶來巨大損失;而在《帶着問題學習分佈式系統之數據分片》中提到的元數據管理,一致性就很是重要,否則就亂套了。數據庫
爲了解決第二個問題,人們提出了不少不一樣的副本控制協議,即在分佈式系統中,按照特定的流程規範來讀寫副本數據,使得副本知足必定的可用性和一致性的要求。緩存
副本控制協議有不少,也有不一樣的分類標準,好比:同步與異步、強一致性與弱一致性、中心化與去中心化。本文主要介紹中心化副本控制協議,在講解不一樣的系統實現時,也分提到在同步與異步、強一致性與弱一致性方面的選擇。服務器
本文地址:http://www.cnblogs.com/xybaby/p/7153755.html網絡
所謂的中心化,就是對於副本集的更新操做有一箇中心節點來協調管理,將分佈式的併發操做轉化爲單點的併發操做,從而保證副本集內各節點的一致性。其優勢在於邏輯簡單,將複雜的問題(分佈式併發)轉換成一個有成熟解決方案的問題(單點併發)。但缺點在於,副本集的可用性依賴於中心節點,若是中心節點故障,即便有中心節點自動切換機制,也會出現數10秒的不可用。併發
大多數的分佈式存儲都會採用中心化副本控制協議,好比GFS,TFS,MongoDB負載均衡
而去中心化則是說副本集中沒有中心節點,全部節點的地位是平等的,你們均可以接受更新請求,相互經過協商達成數據的一致。去中心化副本控制協議的最大好處在於可用性比較強,只要有大多數節點存活就能提供服務。但缺點時協議流程複雜,尤爲是須要強一致性保證的時候。異步
在業界中,Dynamo,cassandra就是基於去中心化協議,雖然 Dynamo 嘗試經過引入 Quorum 機制和 vector clock 機制解決讀取數據的一致性問題,但其一致性模型依舊是一個較大的問題。
本文主要對中心化副本控制協議進行詳細介紹。
中心化副本控制協議在分佈式存儲系統中使用很是規範,但各家實現又有不一樣。這裏主要集合分佈式文件系統與分佈式數據庫來作對比分析
回顧《帶着問題學習分佈式系統》一文,對於中心化副本控制協議,提出瞭如下疑問,本章節試圖給出解答。
(3.1)寫節點怎麼將變動的數據同步到其餘節點,同步仍是異步;
(3.2)非寫節點可否提供讀數據,若是可以容許,會不會讀取到過期的數據。(3.3)主節點是怎麼產生的,當主節點宕機的時候,怎麼選擇出新的主節點。是有統一的複製集管理中心(記錄誰主誰次,各自的狀態),仍是複製集本身選舉出一個主節點?
第一個問題:複製集之間數據的同步是同步模式仍是異步模式。
在中心化副本控制協議中,主節點(primary)提供寫入操做,數據會同步到其餘節點。注意,上面語句中第一個同步是指複製集中節點間數據趨於一致的過程。
所謂同步(Synchronous replication),就是說對於客戶端請求,系統阻塞到複製集中全部節點都更新完成,才能向客戶端返回,即write all。而異步(Asynchronous replication)模式,只要一個或者部分節點更新則算寫入操做成功,一般是write one。
上圖(來源於Distributed systems for fun and profit,下同)即爲同步模式,客戶端的請求被髮送到s1這個副本集,s1將請求轉發給s二、s3,等s二、s3都操做完成以後再向客戶端返回結果。
在同步模式下,系統的可靠性很是好,只要有一個節點正常,就能保證數據不丟失。可是系統的更新可用性很是差,只要有一個節點異常,就沒法完成更新;並且,響應延遲比較大,取決於副本集中網絡延時最大、處理速度最慢的節點。
上圖則是異步模式,客戶端的寫請求只要在一個節點上完成就當即向客戶端返回結果。在上圖中,數據被寫入到s1即認爲寫入成功,向客戶端返回,系統在後臺由s1向s二、s3節點同步數據。
異步模式下,系統的吞吐量會比較好,可是存在數據丟失的風險,好比上圖中,若是在數據同步到s2 s3以前s1掛掉,那麼剛纔客戶端的更新就丟失了,關鍵在於客戶端認爲已經寫入成功了。另外,異步模式下,客戶端在寫入成功以後,馬上從系統讀取數據,有可能讀不到最新的數據,好比上圖中,客戶端寫入s1以後馬上從s2 讀取。
在數據同步的時候選擇同步模式仍是異步模式呢,這個取決於系統對一致性、可用性、響應延遲的要求。
好比在分佈式文件系統GFS中,須要保證複製集內副本的強一致性,而單次讀寫的響應延遲並無那麼重要,所以選擇了同步模式,即primary須要等到全部的secondary都寫入成功纔會向客戶端返回。
而在分佈式數據庫MongoDB中,決定權交給了用戶,用戶能夠決定使用同步模式仍是異步模式。在《CAP理論與MongoDB一致性、可用性的一些思考》一文中詳細介紹了writeconcern這個寫入選項。若是w:1.那麼只會寫入到primary節點就當即返回,系統會在後臺向secondary節點同步數據,即爲異步模式。若是w:N(N爲複製集節點數目),那麼primary節點須要等到全部secondary都更新到最新的數據以後(或者等待超時)才向客戶端返回,也就是同步模式。即便在去中心化副本控制協議,如cassandra,也提供給用戶自行設定一致性等級。
前面已經提到了同步模式、異步模式各自的優劣,這裏以MongoDB爲例具體討論,看看同步、異步模式對系統一致性、可用性的影響。
在異步模式(w: 1)下,系統的響應延遲很低,可用性很是好,但存在兩個問題。第一:同一個客戶端在獲得成功寫入的返回以後當即從secondary節點讀取,有可能讀不到最新的數據;第二:在主從切換的時候(後面會詳細講解這一過程),可能發生rollback,簡單來講,數據只持久化到了primary,secondary節點還未更新到最新的數據,此時若是primary故障,系統會選舉出新的primary,即便舊的primary恢復正常後以secondary身份從新加入複製集,新的primary不會認可其數據,這就致使了更新丟失的問題。
同步模式下(w:N或者w:Majority),須要等待全部節點都寫入成功,響應延遲會比較高,在數據庫應用中通常很難接受,以前基於《經過一步步建立sharded cluster來認識MongoDB》中的複製集(一個primary、一個secondary、一個arbiter)作過實驗,若是將secondary shutdown(db.shutdownServer),而後用writeConcern: {w: "majority」, wtimeout: 10}寫入數據,客戶端會阻塞到超時,而後給出超時信息。不過,w:majority保證了寫入的數據不會丟失,即不會出現rollback的問題。
第二個問題:數據的流向
即數據是如何從Primary節點到secondary節點的。
首先是比較有意思的GFS,GFS寫入流程以下:
GFS的寫入將控制流與數據流分開,客戶端會把數據流鏈式推送到各個節點,推送的過程並不關心誰是primary、誰是secondary。須要注意的是,各節點(GFS中稱之爲chunkserver)只是緩存數據,等到Primary向secondary發送持久換指令(step5)的時候再回真正持久化寫入。
更通常的,數據的流向有兩種,鏈式與主從模式。
鏈式就是指從一個節點推送到最近的節點,好比GFS,「最近」 能夠用IP地址或者節點間心跳TTL來衡量,如圖所示(圖片來源於清華阿里-大數據課程的PPT):
不難看出,寫入過程當中每一個節點的帶寬利用都比較均衡,能夠充分利用網絡資源,也不會有單點壓力;可是須要通過多個節點,寫入延遲會比較大。
而主從模式則是指數據同時從primary節點到secondary節點,如圖所示(來源)
默認狀況下MongoDB也是採用的鏈式模式,可是能夠經過設置 settings.chainingAllowed = false 來採用主從模式。在主從模式下,Secondary會從Primary拉取OPLOG並應用到本地。顯然,在這種模式下Primary節點的帶寬壓力比較大,可是寫入延遲會小一些。
第三個問題:部分節點寫入失敗了怎麼辦
無論是同步模式仍是異步模式,也無論是鏈式推送仍是主從模式推送,複製集中數據的寫入都是1PC(1 phase commit)。即數據的更新只有一個commit階段,而沒有prepare階段,若是某些節點發生故障,那麼提交在故障節點上會失敗,甚至是提交了部分、不完整的數據。
複製集中多個節點的更新本質上來講應該是分佈式事務問題,理論上應該保證原子行:要麼都更新成功,要麼都不更新,而不會出現部分節點更新成功的狀況。但經典解決方案如兩階段提交代價太大,所以分佈式存儲中的複製集更新大多采用best effort 1pc,只不過不一樣的系統對更新失敗的處理有所區別。
好比MongoDB,以第一個問題中提到的例子,writeconcern爲w: majority,因爲其中一個secondary掛掉,寫入操做是不可能成功的。所以,在超時時間到達以後,會向客戶端返回出錯信息。可是在這個時候直接鏈接到rs的primary節點,數據是持久化到了primary節點,不會被回滾。
另外,對於分佈式圖片存儲haystack,若是更新失敗,會重試流程,直到成功或超時,重試的話全部節點都會重試。那麼可能出現兩個問題,某個節點上數據部分寫入(寫入部分數據就崩潰了);因爲重試是對複製集中全部節點重試,所以某個節點上同一份數據可能寫入了多份。對於第一個問題,因爲有checksum,所以不怕部分寫入失敗;第二個問題,因爲有offset和元數據,重複寫入也不是問題。haystack(以及GFS)經過這種巧妙的方式解決了分佈式更新問題。
複製集中,不一樣的系統在數據讀取方面有兩個問題。第一:secondary節點是否提供讀服務;第二,若是能夠從Secondary讀取,那麼這個接口是否開放給用戶
第一個問題,若是secondary節點提供數據讀取服務,那麼是否會讀取到過時的數據(即不是最新成功寫入的數據) ?好比在異步寫入的時候,客戶端獲得成功寫入的返回以後,當即去secondary上讀取,那麼有可能讀到過期的數據,這對於強一致性的狀況是不能容許的。咱們知道,元數據的管理通常也是複製集,而元數據須要保證強一致性,所以,元數據的寫入通常都是同步的。好比GFS中,master由一個active(也就是primary節點)、多個standby(也就是secondary節點)組成,在元數據寫入到active的時候,要保證本地和遠程機器都寫入成功才能返回;並且只有active提供讀取服務。
第二個問題,若是複製集中的節點都能提供讀取服務,那麼接口是否提供給最終用戶呢?在haystack中,多個在不一樣機器上的物理卷組成一個邏輯卷,一個邏輯卷就是一個複製集。當讀取請求到達的時候,是由haystack的元數據服務器(directory )根據負載均衡的原則選出提供服務的物理卷,即用戶是不知道讀取請求是落地到哪一個物理節點的。而對於mongodb,用戶能夠在查詢語句裏面指定是從Primary讀取,仍是從Secondary讀取,或者讓系統來選擇(Nearest)。
讀取方式與用戶角度的一致性很是相關,好比在MongoDB中,不一樣的readrefence致使一致性、可用性的差別,具體可見《CAP理論與MongoDB一致性、可用性的一些思考》
在中心化副本控制協議中,這個中心(primary)是怎麼選出來的呢?是上級指定仍是民主選舉呢?
GFS系統中,Primary節點是由master(GFS中的元數據服務器)經過lease機制選擇的,關於Lease機制的,能夠參見《帶着問題學習分佈式系統之數據分片》一文中相關章節的介紹。簡單說來,GFS給某個節點頒發Lease,該節點就成爲了Primary節點,Primary節點也能夠在過時以前從新申請Lease,並且Lease的頒發、申請信息都是在chunkserver與master的心跳中,所以也不會帶來過多額外的開銷。使用Lease機制能很好的避免在複製集中出現雙主(同時有兩個節點認爲本身是Primary)現象。
而在Zookeeper、TFS、MongoDB中,都是經過去中心化的協議選舉出Primary節點,選舉出Primary節點以後,就變成了中心化的副本控制協議,當Primary出現故障以後,會從新選舉過程。對於民主選舉,兩個因素很是重要:第一是強一致性,只能選舉出一個Primary;第二個是可用性,選舉過程要越快越好。
爲了達到強一致性,須要使用分佈式一致性協議,目前較爲常見的協議有Paxos協議,該協議能夠實現全部備份都可以提供對外服務,而且保證強一致性,經過理論和實踐檢驗能夠達到分佈式的要求。Raft協議則是Paxos的一種特化,在這個協議的實現中,備份間須要區分主從角色,只有主節點能夠提供對外服務,協議實現簡單高效,能很容易的同各類分佈式數據一致性同步場景相結合,是工程實現最好的選擇。
在mongodb3.2中,Primary選舉算法改爲了raft-like算法,此舉的目的也是爲了縮短選舉的時間,具體可見Replica Set Protocol Version。
在TFS中,meta server(元數據服務器)也是經過raft協議選舉primary的,下面兩個gif形象展現了primary初始選舉以及當primary故障以後的從新選舉(圖片來源於清華阿里-大數據課程的PPT)。
上圖展現了Primary選舉過程
上圖展現了在原來的Primary掛掉以後(也可能僅僅是失去響應),剩餘的節點是如何選舉出新的Primary。
爲了防止出現雙主的狀況,在投票過程當中至少要有超過半數的節點贊成才能選出Primary,這也是爲何複製集中節點數目都是奇數個的緣由。
本文介紹了在分佈式存儲領域使用得比較普遍的中心化副本控制協議,經過不一樣的系統回答了在具體實現方便上的一些選擇。不過,對於具體的系統以及相關的協議,並無很深刻介紹,感興趣的讀者能夠查閱相關連接。
劉傑:分佈式原理介紹
Distributed systems for fun and profit
經典論文翻譯導讀之《Finding a needle in Haystack: Facebook’s photo storage》
大數據平臺核心技術(裏面有TFS的介紹)