分佈式技術

一、分佈式系統的難點

分佈式系統比起單機系統存在哪些難點呢?

1. 網絡因素

由於服務和數據分佈在不同的機器上,每次交互都需要跨機器運行,這帶來如下幾個問題:

1. 網絡延遲:性能、超時

同機房的網絡IO還是比較塊的,但是跨機房,尤其是跨IDC,網絡IO就成爲不可忽視的性能瓶頸了。並且,延遲不是帶寬,帶寬可以隨便增加,千兆網卡換成萬兆,只是成本的問題,但延遲是物理限制,基本不可能降低。

這帶來的問題就是系統整體性能的降低,會帶來一系列的問題,比如資源的鎖住,所以系統調用一般都要設置一個超時時間進行自我保護,但是過度的延遲就會帶來系統的RPC調用超時,引發一個令人頭疼的問題:分佈式系統調用的三態結果:成功、失敗、超時。不要小看這個第三態,這幾乎是所有分佈式系統複雜性的根源。

針對這個問題有一些相應的解決方案:異步化,失敗重試。 而對於跨IDC數據分佈帶來的巨大網絡因素影響,則一般會採用數據同步,代理專線等處理方式。

2. 網絡故障:丟包、亂序、抖動。

這個可以通過將服務建立在可靠的傳輸協議上來解決,比如TCP協議。不過帶來的是更多的網絡交互。因此是性能和流量的一個trade off。這個在移動互聯網中更需要考慮。

2. 魚與熊掌不可兼得——CAP定律

CAP理論是由Eric Brewer提出的分佈式系統中最爲重要的理論之一:

  1. Consistency:[強]一致性,事務保障,ACID模型。

  2. Availiablity:[高]可用性,冗餘以避免單點,至少做到柔性可用(服務降級)。

  3. Partition tolerance:[高]可擴展性(分區容忍性):一般要求系統能夠自動按需擴展,比如HBase。

CAP原理告訴我們,這三個因素最多隻能滿足兩個,不可能三者兼顧。對於分佈式系統來說,分區容錯是基本要求,所以必然要放棄一致性。對於大型網站來說,分區容錯和可用性的要求更高,所以一般都會選擇適當放棄一致性。對應CAP理論,NoSQL追求的是AP,而傳統數據庫追求的是CA,這也可以解釋爲什麼傳統數據庫的擴展能力有限的原因。

在CAP三者中,“可擴展性”是分佈式系統的特有性質。分佈式系統的設計初衷就是利用集羣多機的能力處理單機無法解決的問題。當需要擴展系統性能時,一種做法是優化系統的性能或者升級硬件(scale up),一種做法就是“簡單”的增加機器來擴展系統的規模(scale out)。好的分佈式系統總在追求”線性擴展性”,即性能可以隨集羣數量增長而線性增長。

可用性和可擴展性一般是相關聯的,可擴展行好的系統,其可用性一般會比較高,因爲有多個服務(數據)節點,不是整體的單點。所以分佈式系統的所有問題,基本都是在一致性與可用性和可擴展性這兩者之間的一個協調和平衡。對於沒有狀態的系統,不存在一致性問題,根據CAP原理,它們的可用性和分區容忍性都是很高,簡單的添加機器就可以實現線性擴展。而對於有狀態的系統,則需要根據業務需求和特性在CAP三者中犧牲其中的一者。一般來說,交易系統類的業務對一致性的要求比較高,一般會採用ACID模型來保證數據的強一致性,所以其可用性和擴展性就比較差。而其他大多數業務系統一般不需要保證強一致性,只要最終一致就可以了,它們一般採用BASE模型,用最終一致性的思想來設計分佈式系統,從而使得系統可以達到很高的可用性和擴展性。

CAP定律其實也是衡量分佈式系統的重要指標,另一個重要的指標是性能。

一致性模型

主要有三種:

  1. Strong Consistency(強一致性):新的數據一旦寫入,在任意副本任意時刻都能讀到新值。比如:文件系統,RDBMS,Azure Table都是強一致性的。

  2. Week Consistency(弱一致性):不同副本上的值有新有舊,需要應用方做更多的工作獲取最新值。比如Dynamo。

  3. Evantual Consistency(最終一致性):一旦更新成功,各副本的數據最終將達到一致。

從這三種一致型的模型上來說,我們可以看到,Weak和Eventually一般來說是異步冗餘的,而Strong一般來說是同步冗餘的(多寫),異步的通常意味着更好的性能,但也意味着更復雜的狀態控制。同步意味着簡單,但也意味着性能下降。

以及其他變體:

  1. Causal Consistency(因果一致性):如果Process A通知Process B它已經更新了數據,那麼Process B的後續讀取操作則讀取A寫入的最新值,而與A沒有因果關係的C則可以最終一致性。

  2. Read-your-writes Consistency(讀你所寫一致性):如果Process A寫入了最新的值,那麼 Process A的後續操作都會讀取到最新值。但是其它用戶可能要過一會纔可以看到。

  3. Session Consistency(會話一致性):一次會話內一旦讀到某個值,不會讀到更舊的值。

  4. Monotonic Read Consistency(單調一致性):一個用戶一旦讀到某個值,不會讀到比這個值更舊的值,其他用戶不一定。

等等。

其中最重要的變體是第二條:Read-your-Writes Consistency。特別適用於數據的更新同步,用戶的修改馬上對自己可見,但是其他用戶可以看到他老的版本。Facebook的數據同步就是採用這種原則。

二、分佈式系統常用技術和應用場景

  • consistent hashing [with virtual node]:一致性哈希,數據分佈

  • vector clock:時鐘向量,多版本數據修改

  • Quorum W+R>N [with vector clock]:抽屜原理,數據一致性的另一種解決方案。時鐘向量,多版本數據修改。

  • Merkle tree [with anti-entropy]:數據複製

  • MVCC:copy-on-write與snapshot

  • 2PC/3PC:分佈式事務

  • Paxos:強一致性協議

  • Symmetry and Decentralization:對稱性和去中心化。對稱性(symmetry)簡化了系統的配置和維護。去中心化是對對稱性的延伸,可以避免master單點,同時方便集羣scale out。

  • Map-Reduce:分而治之;移動數據不如移動計算。將計算儘量調度到與存儲節點在同一臺物理機器上的計算節點上進行,這稱之爲本地化計算。本地化計算是計算調度的一種重要優化。

  • Gossip協議:節點管理

  • Lease機制:

consistent hashing:一致性哈希,解決數據均衡分佈問題

我們通常使用的hash算法是hash() mod n,但是如果發生某個節點失效時,無法快速切換到其他節點。爲了解決單點故障的問題,我們爲每個節點都增加一個備用節點,當某個節點失效時,就自動切換到備用節點上,類似於數據庫的master和slave。但是依然無法解決增加或刪除節點後,需要做hash重分佈的問題,也就是無法動態增刪節點。這時就引入了一致性hash的概念 ,將所有的節點分佈到一個hash環上,每個請求都落在這個hash環上的某個位置,只需要按照順時針方向找到的第一個節點,就是自己需要的服務節點。當某個節點發生故障時,只需要在環上找到下一個可用節點即可。

consistent hashing

一致性hash算法最常用於分佈式cache中,比如注意的memcached。Dynamo也用其作爲數據分佈算法,並且對一致性算法進行了改進,提出了基於虛擬節點的改進算法,其核心思路是引入虛擬節點,每個虛擬節點都有一個對應的物理節點,而每個物理節點可以對應若干個虛擬節點。

關於一致性hash的更多內容,可以參考筆者另一篇博文:Memcached的分佈式算法學習

這篇文章也可以看看:某分佈式應用實踐一致性哈希的一些問題

virtual node

前面說過,有的Consistent Hashing的實現方法採用了虛擬節點的思想。使用一般的hash函數的話,服務器的映射地點的分佈非常不均勻。因此,使用虛擬節點的思想,爲每個物理節點(服務器)在continuum上分配100~200個點。這樣就能抑制分佈不均勻,最大限度地減小服務器增減時的緩存重新分佈。

Quorum W+R>N:抽屜原理,數據一致性的另一種解決方案

N: 複製的節點數,即一份數據被保存的份數。 R: 成功讀操作的最小節點數,即每次讀取成功需要的份數。 W: 成功寫操作的最小節點數 ,即每次寫成功需要的份數。

所以 W+R>N的意思是:對於有N份拷貝的分佈式系統,寫到W(W<=N)份成功算寫成功,讀R(R<=N)份數據算讀成功。

這三個因素決定了可用性,一致性和分區容錯性。W+R>N可以保證數據的一致性(C),W越大數據一致性越高。這個NWR模型把CAP的選擇權交給了用戶,讓用戶自己在功能,性能和成本效益之間進行權衡。

對於一個分佈式系統來說,N通常都大於3,也就說同一份數據需要保存在三個以上不同的節點上,以防止單點故障。W是成功寫操作的最小節點數,這裏的寫成功可以理解爲“同步”寫,比如N=3,W=1,那麼只要寫成功一個節點就可以了,另外的兩份數據是通過異步的方式複製的。R是成功讀操作的最小節點數,讀操作爲什麼要讀多份數據呢?在分佈式系統中,數據在不同的節點上可能存在着不一致的情況,我們可以選擇讀取多個節點上的不同版本,來達到增強一致性的目的。

NWR模型的一些設置會造成髒數據和版本衝突問題,所以一般要引入vector clock算法來解決這個問題。

需要保證系統中有max(N-W+1,N-R+1)個節點可用。

關於NWR模型,建議閱讀 分佈式系統的事務處理,寫的很通俗易懂。

vector clock:時鐘向量,多版本數據修改

參見 分佈式系統的事務處理,寫的很通俗易懂。

lease機制

chubby、zookeeper 獲得lease(租約)的節點得到系統的承諾:在有效期內數據/節點角色等是有效的,不會變化的。

lease機制的特點:

  • lease頒發過程只需要網絡可以單向通信,同一個lease可以被頒發者不斷重複向接受方發送。即使頒發者偶爾發送lease失敗,頒發者也可以簡單的通過重發的辦法解決。

  • 機器宕機對lease機制的影響不大。如果頒發者宕機,則宕機的頒發者通常無法改變之前的承諾,不會影響lease的正確性。在頒發者機恢復後,如果頒發者恢復出了之前的lease 信息,頒發者可以繼續遵守lease的承諾。如果頒發者無法恢復lease信息,則只需等待一個最大的lease超時時間就可以使得所有的lease都失效,從而不破壞lease機制。

  • lease機制依賴於有效期,這就要求頒發者和接收者的時鐘是同步的。

    • 如果頒發者的時鐘比接收者的時鐘慢,則當接收者認爲lease已經過期的時候,頒發者依舊認爲lease有效。接收者可以用在lease到期前申請新的lease的方式解決這個問題。

    • 如果頒發者的時鐘比接收者的時鐘快,則當頒發者認爲lease已經過期的時候,可能將lease頒發給其他節點,造成承諾失效,影響系統的正確性。對於這種時鐘不同步,實踐中的通常做法是將頒發者的有效期設置得比接收者的略大,只需大過時鐘誤差就可以避免對lease的有效性的影響。

工程中,常選擇的lease時長是10秒級別,這是一個經過驗證的經驗值,實踐中可以作爲參考並綜合選擇合適的時長。

雙主問題(腦裂問題)

lease機制可以解決網絡分區問題造成的“雙主”問題,即所謂的“腦裂”現象。配置中心爲一個節點發放lease,表示該節點可以作爲primary節點工作。當配置中心發現primary有問題時,只需要等到前一個primary的lease過期,就可以安全地頒發新的lease給新的primary節點,而不會出現“雙主”問題。 在實際系統中,若用一箇中心節點作爲配置中心發送lease也有很大的風險。實際系統總是使用多箇中心節點互爲副本,成爲一個小的集羣,該小集羣具有高可用性,對外提供頒發lease的功能。chubby和zookeeper都是基於這樣的設計。

chubby一般有五臺機器組成一個集羣,可以部署成兩地三機房。chubby內部的五臺機器需要通過Paxos協議選取一個chubby master機器,其它機器是chubby slave,同一時刻只有一個chubby master。chubby相關的數據,比如鎖信息,客戶端的session信息等都需要同步到整個集羣,採用半同步的做法,超過一半的機器成功就可以回覆客戶端。最後可以確保只有一個和原有的chubby master保持完全同步的chubby slave被選取爲新的chubby master。

Gossip協議

Gossip用於P2P系統中自治節點獲悉對集羣認識(如集羣的節點狀態,負載情況等)。 系統中的節點定期互相八卦,很快八卦就在整個系統傳開了。 A、B兩個節點八卦的方式主要是:A告訴B知道哪些人的什麼八卦;B告訴A這些八卦裏B知道哪些更新了;B更新A告訴他的八卦...... 說是自治系統,其實節點中還有一些種子節點。種子節點的作用主要是在有新節點加入系統時體現。新節點加入系統中,先與種子節點八卦,新節點獲得系統信息,種子節點知道系統中多了新節點。其他節點定期與種子節點八卦的時候就知道有新節點加入了。 各個節點互相八卦的過程中,如果發現某個節點的狀態很長時間都沒更新,就認爲該節點已經宕機了。

Dynamo使用了Gossip協議來做會員和故障檢測。

2PC、3PC、Paxos協議: 分佈式事務的解決方案

分佈式事務很難做,所以除非必要,一般來說都是採用最終一致性來規避分佈式事務。

目前底層NoSQL存儲系統實現分佈式事務的只有Google的系統,它在Bigtable之上用Java語言開發了一個系統 Megastore,實現了兩階段鎖,並通過Chubby來避免兩階段鎖協調者宕機帶來的問題。Megastore實現目前只有簡單介紹,還沒有相關論文。

2PC

實現簡單,但是效率低,所有參與者需要block,throughput低;無容錯,一個節點失敗整個事務失敗。如果第一階段完成後,參與者在第二階沒有收到決策,那麼數據結點會進入“不知所措”的狀態,這個狀態會block住整個事務。

3PC

改進版的2PC,把2PC的第一個段break成了兩段: 詢問,然後再鎖資源,最後真正提交。3PC的核心理念是:在詢問的時候並不鎖定資源,除非所有人都同意了,纔開始鎖資源。

3PC比2PC的好處是,如果結點處在P狀態(PreCommit)的時候發生了Fail/Timeout的問題,3PC可以繼續直接把狀態變成C狀態(Commit),而2PC則不知所措。

不過3PC實現比較困難,而且無法處理網絡分離問題。如果preCommit消息發送後兩個機房斷開,這時候coordinator所在的機房會abort,剩餘的participant會commit。

Paxos

Paxos的目的是讓整個集羣的結點對某個值的變更達成一致。Paxos算法是一種基於消息傳遞的一致性算法。Paxos算法基本上來說是個民主選舉的算法——大多數的決定會成個整個集羣的統一決定。

任何一個點都可以提出要修改某個數據的提案,是否通過這個提案取決於這個集羣中是否有超過半數的結點同意(所以Paxos算法需要集羣中的結點是單數)。這個是Paxos相對於2PC和3PC最大的區別,在2f+1個節點的集羣中,允許有f個節點不可用。

Paxos的分佈式民主選舉方式,除了保證數據變更的一致性之外,還常用於單點切換,比如Master選舉。

Paxos協議的特點就是難,both 理解 and 實現 :(

關於2PC,3PC和Paxos,強烈推薦閱讀 分佈式系統的事務處理

目前大部分支付系統其實還是在2PC的基礎上進行自我改進的。一般是引入一個差錯處理器,進行差錯協調(回滾或者失敗處理)。

MVCC:多版本併發控制

這個是很多RDMS存儲引擎實現高併發修改的一個重要實現機制。具體可以參考:

  1. 多版本併發控制(MVCC)在分佈式系統中的應用

  2. MVCC (Oracle, Innodb, Postgres).pdf

Map-Reduce思想

1. 分而治之

2. 移動數據不如移動計算

如果計算節點和存儲節點位於不同的物理機器則計算的數據需要通過網絡傳輸,此種方式的開銷很大。另一種思路是,將計算儘量調度到與存儲節點在同一臺物理機器上的計算節點上進行,這稱之爲本地化計算。本地化計算是計算調度的一種重要優化。

經典論文和分佈式系統學習

Dynamo

HBase

LSM Tree

  • LSM(Log Structured Merge Trees)是B+ Tree一種改進

  • 犧牲了部分讀性能,用來大幅提高寫性能

  • 思路:拆分樹

    • 首先寫WAL,然後記錄數據到入到內存中,構建一顆有序子樹(memstore)

    • 隨着子樹越來越大,內存的子樹會flush到磁盤上(storefile)

    • 讀取數據:必須遍歷所有的有序子樹(不知數據在哪棵子樹)

    • Compact:後臺線程對磁盤中的子樹進行歸併,變成大樹(子樹多了讀得慢)

事實上,lucene的索引機制也類似Hbase的LSM樹。也是寫的時候分別寫在單獨的segment,後臺進行segement合併。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章