這一部分,與分佈式不大相關,記錄的是我是如何在分佈式學習這條道路上摸索的,不感興趣的讀者請直接跳到下一章。php
過去的一年,我在分佈式學習這條道路上苦苦徘徊,始終沒有找到一個好的學習方法,一個能激勵我持續學習的方法。html
固然,我也在不停的反思,最早發現的問題是學習不繫統,東看看,西看看,看過就忘。因而寫了一篇文章《帶着問題學習分佈式系統》提出了一些問題,而後寫了兩篇文章來回帶本身提出的問題。回過頭看,其實這兩篇文章都是基於分佈式存儲的。node
而後接下來,又是一段時間的迷茫,不知道要學習啥(也是在學習Paxos的時候遇到了阻礙),偶然看到了關於SMART的一些文章,發現原來本身的學習一點都不SAMRT,因此堅持不下去。因而又寫了一篇文章《帶着SMART原則從新出發》,但願給本身設定SMART的目標,結果被博友打臉「一點都不smart」,並且也被結果打臉了,確實沒有讓我「從新出發」。算法
我也曾經在網上搜索,「如何學習分佈式系統「,但並無找到理想的、或者說適合個人學習方法。網上的答案,都是說,看這些論文,看這些系統的實現。。。或者不少大學的課程,都是羅列出一堆術語:概念、協議、組件。而我以爲,逐個去了解這些術語彷佛很無聊,由於不知道爲何要這個協議,或者說這些協議、組件是如何組合起來的。mongodb
在沒有獲得適合本身的方案以後,我本身思考,但願更系統、全面的掌握,因而寫了這篇文章《什麼是分佈式系統,如何學習分佈式系統》。主要內容是,從一個實際的大型網絡出發,思考會遇到哪些問題,須要哪些分佈式協議與組件。寫完這篇文章,個人想法就是,逐個去學習這些組件。緩存
春節的時候,看完了《小強升職記》,裏面寫到要用六個高度與大樹分解法來設定本身的目標,制定本身的執行計劃。因而,我嘗試用這兩個方法來指導我對分佈式的學習。遺憾的是,在大數據分解法 樹幹(月計劃)這一層,我就沒法計劃下去了,不知道每月要學啥。具體緣由,我想,仍是沒有一個對分佈式系統的整理的認識。怎麼總體把握呢,我想起了以前看到過的思惟導圖,因而就想畫一張分佈式系統的思惟導圖,而後逐個擊破。固然,最開始也是瞎畫,當時劃出了5個樹幹,是這樣子的:服務器
在延伸feature(分佈式系統須要考慮的特性)的時候,我逐漸明白,這是由於要知足這些feature,才設計了不少協議與算法,也提出了一些理論。好比說,這是由於要解決去中心化副本的一致性問題,才引入了Paxos(raft)協議。而每個分佈式系統,如分佈式存儲、分佈式計算、分佈式消息隊列、分佈式RPC框架,根據業務的不一樣,會使用不一樣的方法來知足這些feature,對這些feature的支持也可能會有權衡,好比一致性與可用性的權衡。網絡
全部,我以爲從分佈式的特性出發,來一步步學習分佈式是一種可行的方式。架構
分佈式的世界中涉及到大量的協議(raft、2pc、lease、quorum等)、大量的理論(FLP, CAP等)、大量的系統(GFS、MongoDB、MapReduce、Spark、RabbitMQ等)。這些大量的知識老是讓咱們無從下手,任何一個東西都須要花費大量的時間,特別是在沒有項目、任務驅動的時候,沒有一個明確的目標,真的很難堅持下去。併發
因此,我一直在思考,能有什麼辦法能把這些東西串起來?當我掌握了知識點A的時候,可以天然地想到接下來要學習B知識,A和B的關係,也許是遞進的,也許是並列的。我也這樣嘗試了,那就是《什麼是分佈式系統,如何學習分佈式系統》一文中我提到的,思考一個大型網站的架構,而後把這些協議、理論串起來。按照這個想法,個人計劃就是去逐個學習這些組件。
可是,其實在這裏有一個誤區,我認爲一個大型網站就是一個分佈式系統,包含諸多組件,這些組件是分佈式系統的組成部分;而我如今認爲,一個大型網站包含諸多組件,每個組件都是一個分佈式系統,好比分佈式存儲就是一個分佈式系統,消息隊列就是一個分佈式系統。
爲何說從思考分佈式的特徵出發,是一個可行的、系統的、按部就班的學習方式呢,由於:
(1)先有問題,纔會去思考解決問題的辦法
因爲咱們要提升可用性,因此咱們才須要冗餘;因爲須要擴展性,因此咱們才須要分片
(2)解決一個問題,經常會引入新的問題
好比,爲了提升可用性,引入了冗餘;而冗餘又帶來了副本之間的一致性問題,因此引入了中心化副本協議(primary/secondary);那麼接下來就要考慮primary(節點)故障時候的選舉問題。。。
(3)這是一個金字塔結構,或者說,也是一個深度優先遍歷的過程。
在這個過程當中,咱們始終知道本身已經掌握了哪些知識;還有哪些是已經知道,但未了解的知識;也能知道,哪些是空白,即咱們知道這裏可能有不少問題,可是具體是什麼,還不知道。
那麼,各個分佈式系統如何與這些特徵相關聯呢?不難發現,每一個分佈式系統都會或多或少的體現出這些特徵,只是使用的方法、算法可能不大同樣。因此,咱們應該思考,某一個問題,在某個特定系統中是如何解決的。好比元數據管理的強一致性,在MongoDB中是如何實現的,在HDFS中是如何實現的。這也指導了咱們如何去學習一個具體的分佈式系統:帶着問題,只關注關心的部分,而不是從頭至尾看一遍。
下面是,到目前爲止,我對分佈式特徵的思惟導圖
對於上圖,須要聲明的是,第一:不必定徹底正確,第二:不完整。這是由於,我本身也在學習中,能夠看到,不少分支很短(好比去中心化副本協議),不是由於這一塊沒有內容,而是我壓根兒還沒去了解,還沒去學習。
我會持續跟新這幅腦圖的!
下一章,介紹一下分佈式系統的各個特徵。
任何介紹分佈式系統的文章或者書籍都會提到分佈式系統的幾個特性:可擴展性、高性能、高可用、一致性。這幾個特性也是分佈式系統的衡量指標,正是爲了在不一樣的程度上知足這些特性(或者說達到這些指標),纔會設計出各類各樣的算法、協議,而後根據業務的需求在這些特性間平衡。
那麼本章節簡單說明,爲何要知足這些特性,要知足這些特性須要解決什麼問題,有什麼好的解決方案。
Scalability is the capability of a system, network, or process to handle a growing amount of work, or its potential to be enlarged to accommodate that growth.
可擴展性是指當系統的任務(work)增長的時候,經過增長資源來應對任務增加的能力。可擴展性是任何分佈式系統必備的特性,這是由分佈式系統的概念決定的:
分佈式系統是由一組經過網絡進行通訊、爲了完成共同的任務而協調工做的計算機節點組成的系統
分佈式系統的出現是爲了解決單個計算機沒法完成的計算、存儲任務。那麼當任務規模增長的時候,必然就須要添加更多的節點,這就是可擴展性。
擴展性的目標是使得系統中的節點都在一個較爲穩定的負載下工做,這就是負載均衡,固然,在動態增長節點的時候,須要進行任務(多是計算,多是數據存儲)的遷移,以達到動態均衡。
那麼首先要考慮的問題就是,如何對任務進行拆分,將任務的子集分配到每個節點,咱們稱這個過程問題Partition(Sharding)。關於Partition,其實我在《帶着問題學習分佈式系統之數據分片 》一文中有詳細介紹,這裏進行概括總結。
常見的算法包括:哈希(hash),一致性哈希(consistency hash),基於數據範圍(range based)。每一種算法有各自的優缺點,也就有各自的適用場景。
partition key是數據的特徵值,上面提到的任何分片方式都依賴於這個partition key,那麼該如何選擇呢
based on what you think the primary access pattern will be
partition key會影響到任務在分片之間的均衡,並且一些系統中(mongodb)幾乎是不能從新選擇partition key的,所以在設計的時候就得想清楚
提高性能和併發:不一樣的請求分發到不一樣的分片
提升可用性:一個分片掛了不影響其餘的分片
若是一個操做須要跨越多個分片,那麼效率就會很低下,好比數據中的join操做
元數據記錄了分片與節點的映射關係、節點狀態等核心信息,分佈式系統中,有專門的節點(節點集羣)來管理元數據,咱們稱之爲元數據服務器。元數據服務器有如下特色:
高性能:cache
高可用:冗餘 加 快速failover
強一致性(同時只有一個節點對外提供服務)
爲了達到動態均衡,須要進行數據的遷移,如何保證在遷移的過程當中保持對外提供服務,這也是一個須要精心設計的複雜問題。
可用性(Availability)是系統不間斷對外提供服務的能力,可用性是一個度的問題,最高目標就是7 * 24,即永遠在線。但事實上作不到的,通常是用幾個9來衡量系統的可用性,以下如所示:
也就是若是要達到4個9的可用度(99.99%),那麼一年之中只能有52.6分鐘不可用,這是個巨大的挑戰
爲何分佈式系統中必需要考慮可用性呢,這是由於分佈式系統中故障的機率很高。分佈式系統由大量異構的節點和網絡組成,節點可能會crash、斷電、磁盤損壞,網絡可能丟包、延遲、網絡分割。系統的規模放大了出故障的機率,所以分佈式系統中,故障是常態。那麼分佈式系統的其中一個設計目標就是容錯,在部分故障的狀況下仍然對外提供服務,這就是可用性。
冗餘是提升可用性、可靠性的法寶。
冗餘就是說多個節點負責相同的任務,在須要狀態維護的場景,好比分佈式存儲中使用很是普遍。在分佈式計算,如MapReduce中,當一個worker運行異常緩慢時,master會將這個worker上的任務從新調度到其它worker,以提升系統的吞吐,這也算一種冗餘。但存儲的冗餘相比計算而言要複雜許多,所以主要考慮存儲的冗餘。
維護同一份數據的多個節點稱之爲多個副本。咱們考慮一個問題,當向這個副本集寫入數據的時候,怎麼保證併發狀況下數據的一致性,是否有一個節點有決定更新的順序,這就是中心化、去中心話副本協議的區別。
中心化就是有一個主節點(primary master)負責調度數據的更新,其優勢是協議簡單,將併發操做轉變爲順序操做,缺點是primar可能成爲瓶頸,且在primary故障的時候從新選舉會有一段時間的不可用。
去中心化就是全部節點地位平等,都可以發起數據的更新,優勢是高可用,缺點是協議複雜,要保證一致性很難。
提到去中心化,比較有名的是dynamo,cassandra,使用了quorum、vector clock等算法來儘可能保證去中心化環境下的一致性。對於去中心化這一塊,目前還沒怎麼學習,因此下面主要討論中心化副本集。對於中心化副本協議,我在《帶着問題學習分佈式之中心化複製集》一文中也有詳細介紹,這裏簡單概括總結。
primary節點到secondary節點的數據時同步仍是異步,即客戶端是否須要等待數據落地到副本集中的全部節點。
同步的優勢在於強一致性,可是可用性和性能(響應延遲)比較差;異步則相反。
即數據是如何從Primary節點到secondary節點的,有鏈式和主從模式。
鏈式的優勢時充分利用網絡帶寬,減輕primary壓力,但缺點是寫入延遲會大一些。GFS,MongoDB(默認狀況下)都是鏈式。
理論上,副本集中的多個節點的數據應該保持一致,所以多個數據的寫入理論上應該是一個事務:要麼都發生,要麼都不發生。可是分佈式事務(如2pc)是一個複雜的、低效的過程,所以副本集的更新通常都是best effort 1pc,若是失敗,則重試,或者告訴應用自行處理。
在中心化副本協議中,primary節點是如何選舉出來的,當primary節點掛掉以後,又是如何選擇出新的primary節點呢,有兩種方式:自治系統,依賴其餘組件的系統。(ps,這兩個名字是我杜撰的 。。。)
所謂的自治系統,就是節點內部自行投票選擇,好比mongodb,tfs,zookeeper
依賴其餘組件的系統,是指primary由副本集以後的組件來任命,好比GFS中的primary由master(GFS的元數據服務器)任命,hdfs的元數據namenode由zookeeper心跳選出。
中心化複製集中,secondary是否對外提供讀服務,取決於系統對一致性的要求。
好比前面介紹到節點更新策略時,多是異步的,那麼secondary上的數據相比primary會有必定延遲,從secondary上讀數據的話沒法知足強一致性要求。
好比元數據,須要強一致性保證,因此通常都只會從primary讀數據。並且,通常稱主節點爲active(master),從節點爲standby(slave)。在這種狀況下,是經過冗餘 加上 快速的failover來保證可用性。
從上面能夠看到,爲了高可用性,引入了冗餘(副本)機制,而副本機制就帶來了一致性問題。固然,若是沒有冗餘機制,或者不是數據(狀態)的冗餘,那麼不會出現一致性問題,好比MapReduce。
一致性與可用性在分佈式系統中的關係,已經有足夠的研究,造成了CAP理論。CAP理論就是說分佈式數據存儲,最多隻能同時知足一致性(C,Consistency)、可用性(A, Availability)、分區容錯性(P,Partition Tolerance)中的二者。但一致性和可用性都是一個度的問題,是0到1,而不是隻有0和1兩個極端。詳細能夠參考以前的文章《CAP理論與MongoDB一致性、可用性的一些思考》
一致性從系統的角度和用戶的角度有不一樣的等級。
強一致性、若一致性、最終一致性
單調讀一致性,單調寫一致性,讀後寫一致性,寫後讀一致性
正式由於單個節點的scale up不能完成任務,所以咱們才須要scale out,用大量的節點來完成任務,分佈式系統的理想目標是任務與節點按必定的比例線性增加。
高併發
高吞吐
低延遲
不一樣的系統關注的核心指標不同,好比MapReduce,自己就是離線計算,無需低延遲
單個節點的scaleup
分片(partition)
緩存:好比元數據
短事務
Distributed systems for fun and profit
劉傑:《分佈式系統原理介紹》