分佈式系統概要
昨天晚上看到了一本很有用的小冊子,大約60多頁,名字叫做Distributed systems for fun and profit,內容涉及了分佈式系統的方方面面:有基本的分佈式系統的概念,有複雜的分佈式一致性協議,有關於分佈式系統的擴展性,可用性,可靠性,高吞吐,低延遲的討論。不出意外的話,準備用兩天的時間(打臉了,兩天搞不定啊,東西太多了),把它的全部內容付諸於這篇博客中。事實上,只要在搜索隨便輸入那本書的名字,就可以再網上搜索出一大堆的個人理解,但是最喜歡第一手資料的我還是喜歡讀原文,喜歡寫總結。
聲明:
不喜歡完整的翻譯原文,總感覺全部的翻譯之後總感覺啥都沒說。邊寫邊思考再邊總結,常常能獲得質的提升,但是這無法避免會出現由於個人知識水平或者理解能力的欠缺引起的疏漏或者錯誤,還請諒解。不變的一點是最權威的參考是論文,次權威的參考是原文。本文只希望能夠以個人這幾年來對分佈式系統的淺薄認識能對原文進行一些Chinese類型的直觀理解,沉澱自己,方便他人。
當前完成的部分
[1] 分佈式系統概要(https://blog.csdn.net/lpstudy/article/details/83685997)
[2] 分佈式系統上下層概念抽象(https://blog.csdn.net/lpstudy/article/details/83688141)
還待做的有3,4,5。
1. 總體概覽
全文分爲五大章節,分別涵蓋了分佈式系統的基本概念,分佈式系統的一致性協議,分佈式系統的時序性,副本的強一致性和副本的弱一致性,基本上各個章節都穿插了分佈式系統的多維特徵之間的權衡:可靠性,可用性,故障容錯,一致性,低延遲,高吞吐。話不多說,開始討論。
分佈式系統編程本質上都是在處理兩個公理
帶來的影響
- 消息在系統中以光速傳播
- 獨立的事物,其故障的發生也是獨立的
這兩句話實際上陳述了分佈式系統的基本事實:延遲無法避免,因爲消息傳輸的速度是有限的,雖然是光速,但依然會有latency(其實從網絡的角度,不僅有傳輸時延,還有發送時延);故障的發生是一個常態事件,因爲分佈式系統的集羣規模很大,各種各樣類型的設備都會可能由於掉電,老化,以及軟件層面的bug發生故障,這是一個常態事件,是無法避免的。
爲了更好的理解上面的內容,我們來看兩個假設:
- 假設1:消息以無窮大的速度傳統,也就是沒有時延
我們就可以隨便採用基於consensus的強一致性的協議(例如paxos,raft等),只要一個請求能夠被大部分server響應,則成功。傳統意義上來說,強一致性協議的代價較大(目前paxos需要兩輪的RTT),極大影響系統可用性,而無時延假設恰恰使得我們的系統可用性有保證; - 假設2:系統永遠不會有故障
我們可以採用要求servers全響應語義的一致性協議(例如2PC),只有在所有的server必須全部響應後,纔算做客戶端請求成功,然而實際系統中各式各樣的故障,使得這種要求很難滿足,可用性很難保證。其實上述兩個假設,恰好對應於CAP理論中的P和A,我們知道CAP無法全部滿足,但是對於(a)假設,我們可以構造滿足CP的系統;對於(b)假設,我們可以構造滿足CA的系統,關於CAP的細節,將會在後面的第二章分佈式系統的一致性協議中具體闡述。
上述的兩個限制條件使得我們在分佈式系統的設計中,必須去協調distance,latency以及consistence之間的關係,以設計出滿足現實需求的系統
各章節簡述
- 分佈式系統基本概念
本節講述 - 分佈式系統數據一致性
- 時序性
- 副本的強一致性
- 副本的弱一致性
2. 分佈式系統基本概念
什麼叫作分佈式系統?從宏觀上來看,它本質上是使用多機來解決單機的問題。
Distributed programming is the art of solving the same problem that you can solve on a single computer using multiple computers.
2.1 分佈式系統的基本任務
在Apache Spark的那篇論文的通用性中,我們說到分佈式系統任何
類型的計算任務本質上都可以歸結爲兩個部分,而傳統的MapReduce語義恰好能夠完美匹配這兩個語義。
- Local Computation <—> Map
- Exchange data among different nodes <—> Reduce
同理,你不僅要問,對於一個系統來說,需要滿足哪種最基本的tasks?
- Storage
- Computation
仔細思考,確實如此。所有的系統本質上都是在完成兩種任務,數據存儲和數據轉換。例如數據庫中的數據是存儲,而查詢語言是計算,HDFS是純存儲,Spark是純計算。分佈式系統的設計目標就是要使它從外面來看,更像是一臺機器,而不是多臺機器連接的組合。
2.2 爲何分佈式?
假如你有一臺magical machine,它有足夠多的資源,那麼你完全不需要一個分佈式系統。但是事實上,一臺機器的資源總是有限的。在過去的時代,有不少企業一直在單機性能的道路上奔走,以求打造能處理更大任務的強單機系統,但是最後都宣告失敗,因爲隨着單機性能越強,付出等量的代價帶來的收益比越小。分佈式系統成功的歷史進程告訴我們,用大量廉價的機器打造的系統,可以獲得遠超過使用相同代價構造的單機系統。其實,從CPU的歷史進程與之有很多相似之處,過去的CPU一直在追求單核CPU超高的頻率,核數並不多(我記得2008年,很多cpu才2核心),但是頻率提升到4GHZ之後,發現再向上提升頻率,現有的晶體管的設計就很難滿足了,因此現在爲了提升單機性能,總是以量取勝,雖然單核頻率並不高,但是核數多啊,包括目前的各種大型機的設計,動輒數萬個核心。
讓我們來看看一個證據來說明隨着系統規模的擴大,高性能機器的收益越來越小。
2.3 分佈式系統的設計目標
2.3.1. 可擴展性
is the ability of a system, network, or process, to handle a growing amount of work in a capable manner or its ability to be enlarged to accommodate that growth.
分佈式系統從設計之初就需要考慮以後的擴展,一個好的分佈式系統應該支持線性擴容的原理。也就是說,最好能夠做到任意的增加機器,且增加機器就可以增加系統的對外服務能力,從而可以應對未來任何的業務增長需要。
擴容的3個點:
- 節點可伸縮
也就是說,隨意的增加節點,能夠線性增加系統的吞吐,且不會增加額外的時延; - 跨地區可伸縮
使用多個數據中心來服務外部用戶的訪問,跨數據中心的訪問; - 管理可伸縮
節點等擴容不應該增加系統的管理和維護成本。
2.3.2 高性能
is characterized by the amount of useful work accomplished by a computer system compared to the time and resources used.
當提到高性能
一詞,首先想到的是如下幾點相關的屬性:
- 低延遲
- 高吞吐
- 低資源消耗
我們知道這幾個屬性有很明顯的tradeoff,爲了高吞吐,很多系統設計成batch processing,這樣帶來的代價就是單個work的延遲會比較大。
2.3.3 高可用性
the proportion of time a system is in a functioning condition. If a user cannot access the system, it is said to be unavailable.
分佈式系統相比於單機系統,具有更高的可用性。這是因爲首先故障是無法避免的,對於單機系統來說,如果發生故障,那麼它要不能夠正常處理,要不就crash;而對於分佈式系統來說,可以在不可靠性的硬件之上,構建一個能夠容錯的可靠分佈式系統。那同樣的問題來了,如何度量一個分佈式系統的可用性,它與哪些屬性有關?因此,我們有如下可用性公式:Availability = uptime / (uptime + downtime)
可用性從本質上來說是與故障容錯息息相關的,所謂的不可用一般是指故障不可恢復,從而導致服務不可用。因此,從公式來說,一個系統的可用性就它可用的時間的比率,也就是上面的uptime的比率。通過上面的公式,我們可以給出傳說中的4個9,或者6個9的可用性對應的實際存活時間。
我們可以看到,對於6個9來說,每年只能最多故障31s,那也就是每天不到0.1s的時間。不可否認,故障不是導致不可用的唯一因素,例如依賴的服務關閉了等同樣會引起系統不可用。如果採用這種綜合考慮的辦法,那不確定性因素會有很多,因此,現在系統一般都會對已知的故障做專門的容錯處理。
2.3.4 容錯性
ability of a system to behave in a well-defined manner once faults occur
故障容錯與上面的可用性息息相關,必須在設計之初通過特定的算法作專門的處理。
2.3.5 一致性
2.4 達成目標的痛點
還記得前面說的分佈式系統的兩個公理不? 光速限制以及故障事件的獨立性。我們這裏將這兩個公理
進一步展開爲了分佈式系統的兩個物理要素:
- 節點數目 (對應於分佈式系統的存儲規模和計算能力)
- 節點距離 (對應於分佈式系統的信息傳輸延遲)
帶有這些限制,我們的系統會
- 節點數目的增加會增加系統故障的可能性 (對應於系統的可用性降低和維護成本的提升)
- 節點數目的增加會增加系統的通信代價 (對應於系統的性能降低)
- 跨地區的節點數目的增加會增加系統的傳輸時延 (對應於系統的性能降低)
這些具體限制,會進一步影響我們進行實際系統設計的選擇。那有什麼章法可循不?我們的答案是有。
2.5 模型和抽象
說實話,這兩個詞一出來,就感覺這篇文章的逼格提升了一個level。鑑於自身中文解釋的侷限性,直接貼出別人的定義。
什麼叫抽象?
Abstractions make things more manageable by removing real-world aspects that are not relevant to solving a problem.
什麼叫模型?
Models describe the key properties of a distributed system in a precise manner.
抽象的本質是剝離真實世界的無關屬性,顯示問題的本源;模型是用於將實際問題對應到抽象的概念中。舉個例子來說:爲了舉例,還順帶複習了一把歐拉回路問題
當時東普魯士科尼斯堡(今日俄羅斯加里寧格勒)市區跨普列戈利亞河兩岸,河中心有兩個小島。小島與河的兩岸有七條橋連接。在所有橋都只能走一遍的前提下,如何才能把這個地方所有的橋都走遍? ——Wikipedia
上面的就是著名的科尼斯堡七橋
問題,針對這個問題,歐拉首先將其抽象
爲一個圖,他利用抽象
將真實世界的無關屬性剝離開來,例如橋是邊,兩個橋之間的路是頂點;其次,抽象
之後現實世界的問題就成爲了圖問題
,將這個圖問題進行建模,轉換成的模型
變成:問在這個圖中,是否有一個路徑能夠覆蓋全部的邊,且每個邊不能重複?不知道說明白了沒有,簡單說來抽象是剝離現實世界,模型對現實世界的問題進行建模,以映射到抽象的model上。
對於一個分佈式系統來說,抽象使其更容易被人所理解,但是現實情況下,對外暴露一些細節往往可以做專門的優化,從而提升性能。在後面的介紹中,會涵蓋幾種經典的模型
- 系統模型(同步或者異步)
- 故障模型 (crash-fail, partitions網絡分區, Byzantine拜占庭故障)
- 一致性模型 (strong強一致性,eventual最終一致性)
2.6 設計技巧:partition和replication
這個地方主要講述數據的散佈方式,因爲它直接決定了計算如何定位數據以及操作數據。
對於一個數據集,通常有兩種基本的技術
- 將其劃分成一個個小的分片(partition),這樣可利用分佈式的集羣並行運算能力。
- 將其copy多份以處理系統的故障,或者降低client和server端的延遲(使用local replication服務)
2.6.1 Partition
數據劃分的關鍵在於如何劃分數據,也就是數據劃分的算法,一般是通過hash(key)的方式劃分到不同的node上,這需要一大章節來介紹,本文忽略。
數據劃分帶來的收益:
- 提升系統可用性,減少故障域,因爲分片很小,一個機器的故障只會導致較少的數據不可用;
- 提升系統性能,減少數據處理量,因爲每個機器處理的數據變小了。
注意:上面的第1點從其他的角度來看不一定完全正確,分片越小,則散佈越開。這樣的話,任意故障多個機器,都會導致一個分片的全部副本全丟失,根據copysets的paper計算的概率,這樣發生數據丟失的概率會更高。
2.6.2 Replication
副本簡直是處理故障恢復的的萬能鑰匙。
數據副本的收益:
- 提升系統可用性,需要掛更多的節點纔會導致數據丟失
- 提升系統性能,多個副本可以同時處理或者交給更快的機器處理
分佈式系統採用副本可以獲得可擴展性,高性能,可用性,容錯性
- 害怕數據不可用,採用副本吧,多副本能確保數據由於故障丟失的概率大大降低;
- 計算太慢,採用副本吧,將計算散佈到多臺機器上;
- I/O太慢,採用副本吧,將數據cache到local機器上,可以極大的提升吞吐。
副本帶來一個顯而易見的問題是數據一致性的問題,因爲系統需要維持同一份數據的多份copy,並確保他們的同步。如果希望自己的分佈式系統使用副本就像沒有使用副本那樣,那就採用強一致性協議(在後面的第4章),然而這通常意味着更高的cost;弱一致性協議(在後面的第5章)能夠極大的提高系統的吞吐以及更低的延遲,從而在分佈式系統中得到更大的應用。