分佈式系統的一些基礎理論

一、分佈式系統與 Zookeeper 的關係

1.1 集中式服務

我們先從服務部署架構的發展歷程說起,其實無非就是 集中式分佈式 ,集中式就是說,什麼我都是由一臺機器搞定的。分佈式就是多臺服務器聯合完成。所以在一開始的時候一般都是從一臺服務器開始,將我們的服務部署上去,然後就是一些老套路,Web 應用就部署在 Tomcat 上開放 8080 端口提供服務,然後它需要的一個數據庫服務就開放 3306 端口提供。它的優點就在於結構,部署,項目架構都比較簡單。

然後再根據業務的發展去擴展,那擴展同樣也可以分爲兩種方式,一種是橫向擴展,一種爲縱向擴展。既然一臺搞不定,那就要不提升這個服務器的性能,要不就整多幾臺一起上。但是我們想想,也不是個人就會把服務器安排的服服帖帖的呀,這臺機子一掛,那就全掛了。而且大型主機的購買,還有研發,維護人才,那都是得花大價錢的。這裏給大家擴展一個 “摩爾定律”

反正簡單點來說,就是我花兩倍的錢,根本買不到兩倍的性能。但是橫向擴展就不一樣了,一個人打不過,叫多幾個人一起打不就行了?

1.2 去 IOE 運動

阿里巴巴搞出來的一個口號,具體點就是 IBM 小型機,Oracle 數據庫,EMC 的高端存儲,有興趣的也可以瞭解一下。因爲當時面臨的問題是,企業如果需要提升單機處理能力,成本會很高且性價比極低。還整天怕這怕那的,一宕機就整個服務停掉。慢慢的國內很多公司跟着一起響應,分佈式就起來了。

1.3 分佈式服務

分佈式系統有着它具體的定義:分佈式系統是一個硬件或者軟件組件分佈在不同的網絡計算機上,彼此之間僅通過消息傳遞進行通信和協調的系統。所以就是一堆計算機聯合起來對外提供服務,但是對於用戶來說,像是一臺機子在完成這事。

特點很多,大致就是下面 5 個:

  1. 分佈:這個就是多臺計算機都被放置在了不同的位置

  2. 對等:集羣中的多個工作節點都是一個貨色,乾的都一樣的活兒。而且存在副本概念

  3. 併發:多個機器同時操作一份數據可能會引發的數據不一致問題

  4. 全局時鐘:多個主機上的事件先後順序會對結果產生影響,這也是分佈式場景中非常複雜的一個問題

  5. 各種故障:某節點宕機,網絡不好 ··· 突發情況

1.4 分佈式場景中經常遇到的幾個問題

  1. 通信異常:其實就是網絡問題,導致多節點狀態下數據不一致

  2. 網絡孤立:這個其實就是各個子網絡內部正常,但是整個系統的網絡是不正常的。導致局部數據不一致的問題

  3. 節點宕機問題

  4. 分佈式三態:成功,失敗,超時這 3 種狀態引出的各個問題。請求發送和結果響應都有可能丟失,無法確定消息是否發送 / 處理成功

  5. 數據丟失:這個一般通過副本機制,從其它節點讀取解決,或者對於有狀態的節點來說丟失數據就可以通過恢復狀態來解決。

異常處理原則:任何在設計階段考慮到的異常情況都必須假設一定會在實際運行中發生

1.5 衡量分佈式系統的性能標準

  1. 性能:主要就是吞吐能力,響應延遲,併發能力。系統某一時間可以處理的數據總量,通常是用系統每秒處理的總數據量衡量,而響應延遲指的是完成某一功能所需要的的時間。併發能力就是同時完成某一功能的能力,通常就是用 QPS 衡量

  2. 可用性:在面對各種異常時可以正確提供服務的能力。比如我們常說的 5 個 9 就是指一年內只有 5 分鐘的宕機時間。6 個 9 就是 31 秒

  3. 可擴展性:指可以通過擴大機器規模達到提高系統性能的效果

  4. 一致性:副本管理

但是這些標準都是一個方面要求太高之後會帶動另外一方面變差,比如說我們需要做到高可用,可能需要多個副本,但是多個副本的狀態下,對於數據的一致性又很難去做到了。然後高吞吐下又很難做到低延遲,所以我們需要針對自己的業務場景去進行考量。

1.6 對於一致性的擴展

  1. 強一致性:寫操作完成之後,讀操作一定能讀到最新數據,在分佈式場景中這樣是非常難實現的,比如 Paxos 算法,Quorum 機制,ZAB 協議都是幹這個事的。

  2. 弱一致性:不承諾可以立即讀到寫入的值,也不承諾多久之後數據能夠達到一致,但會盡可能的保證到某個時間級別(比如 XX 時,XX 分,XX 秒後),數據可達到一致性狀態。

它還有一個特例叫做最終一致性,就是儘可能快的保證數據的一致。但是這個快到底是多快,就沒有準確定義了。好比女票想要喫到炸雞,你給點了份外賣,可是美團騎手,餓了嗎騎手也說不準什麼時候送到,他只能說保證儘快送到。就這麼個意思。

因爲最終一致性實在是太弱了所以我們還有一些特例情況會出現讀寫一致性,它是指用戶讀取自己寫入的結果永遠可以第一時間看到自己更新的內容,這個就像微信朋友圈一樣的,我們發出來的東西,微信是一定會讓我們看到的,可是朋友們是不是你發了立刻就能看到,那可就說不準了👀。

還有一些單調讀一致性,因果一致性就不展開說明了,有興趣的小夥伴可以自行搜索。

總而言之,爲了保證系統的高可用,防止單點故障引發的問題,並能夠讓分佈在不同節點上的副本都能正常爲用戶提供服務,這時,我們的 Zookeeper 就應運而生了。它就能幫助我們解決這個分佈式系統中數據一致性的問題

需要解決這個問題我們需要了解分佈式事務,分佈式一致性算法,Quorum 機制,CAP 和 BASE 理論,接下來我們慢慢去展開

二、分佈式事務

事務:單機存儲系統中用來保證存儲系統的數據狀態一致性,這是不是讀起來有點拗口,沒事,我們換個說法,廣義上的事務,就是指一個事情的所有操作,要不全部成功,要不全部失敗,沒有中間狀態。狹義一點,那就是數據庫做的那些操作。特徵也很簡單,就是耳熟能詳的 ACID 。

分佈式系統中每個節點都僅僅知道自己的操作是否成功,但是不知道其它節點是個啥情況,這就有可能導致各節點的狀態可能是不一致的,所以爲了實現跨越多節點且保證事務的 ACID 時,需要引入一個協調者,然後參與事務的各個節點都叫做參與者

典型的套路就是 2PC 和 3PC,接下來我們慢慢展開

2.1 2PC 是個什麼東西

在事務的參與過程中會產生多個角色,暫時我們先這麼理解,協調者負責事務的發起,而參與者負責執行事務。

假定存在上面的 3 個角色,分別是一個協調和兩個參與,此時我們需要 A ,B 執行一個事務,並且要求這個事務,要麼同時成功,要麼同時失敗。

2PC 階段一:執行事務

此時協調者會 先發出一個命令,要求參與者 A,參與者 B 都都去執行這個事務,但是不提交

說的再詳細一點,就會產生寫 redo,undo 的日誌,鎖定資源,執行事務。但是執行完了之後,直接向協調者打報告,詢問一下,大哥我能提交嗎?

這個在日常寫 Java 的過程中應該經常遇到,就是前面寫了一大堆操作,但是等到最後一定會寫一個 conn.commit() 這樣的東西,這就是所謂的 執行但不提交

2PC 階段二:提交事務

當協調者收到第一階段中的所有事務參與者(圖中的 A,B)的反饋(這個反饋簡單理解爲,告訴協調者前面的第一階段執行成功了)時,就發送命令讓所有參與者提交事務

如果要說的再細一點,那就是協調者收到反饋,且所有參與者均響應可以提交,則通知參與者進行 commit,否則 rollback

所以 2PC 也叫做二階段提交,其實就是這麼簡單分成了兩步,一步執行,一步提交。

2PC 的 4 個缺點:性能

整個流程看下來就知道這明顯產生了同步阻塞,各個需要操作數據庫的節點都佔用了數據庫的資源。只有當協調者收到所有節點都準備完畢的反饋,事務協調者纔會通知 commit or rollback,而參與者執行完這個 commit or rollback 的操作後,纔會去釋放資源。

2PC 的 4 個缺點:單點故障

那我們剛剛也知道了,協調者纔是這個事務的核心。假如此時協調者故障宕機,會導致通知無法傳達到參與者的問題,比如收不到那個 commit or rollback ,整一個事務便會停滯。

2PC 的 4 個缺點:數據不一致

協調者在第二階段會發送 commit or rollback。可是這並不能保證每一個節點都正常收到這個命令,所以會可能竄在,參與者 A 收到了命令,提交了事務,但是參與者 B 沒有。所以網絡波動是永恆的病因,你永遠無法躲開這個因素。

2PC 的 4 個缺點:不存在容錯機制

這個協調者需要收到所有的節點反饋準備完成纔會下達 commit 的指示,任意一個參與者的響應沒有收到,協調者就會進行等待,而且只要存在一個宕機的節點,都會使得整個事務失敗回滾。

2.2 3PC 是個啥東西

在 2PC 的前提下進行了一個改良,將 2PC 中的準備階段進行拆分,形成 can commit,pre commit,do commit 三個階段。

並且引入超時機制,一旦事務參與者在指定時間內沒有收到協調者的 commit or rollback 指令,就會自動進行本地 commit,解決協調者的單點故障問題

3PC 第一階段 cancommit

協調者先詢問:哎你們這幫人到底能不能行?參與者就根據自身的實際情況回答 yes or no。

3PC 第二階段 precommit

如果參與者都是返回同意,協調者則向所有參與者發送預提交請求,並進入準備階段,這裏的準備階段其實就是讓參與者鎖定資源,等待指令的意思,然後就是事務的執行,此時也像 2PC 一樣,執行但不提交。然後等待協調者的指令,此時如果遲遲等不到指令,一段時間後就會自行本地提交

但是這樣也會存在弊端,比如協調者成功給 1,2 參與者都發送回滾,然後 3 剛好就沒收到,那麼 3 就自動提交了,所以超時機制其實並不能完全保證數據的一致性

三、分佈式一致性算法

3.1 Paxos 算法

不知道大家有沒有看到我上一年的那篇 從零開始的高併發(三)--- Zookeeper 集羣的搭建和 leader 選舉 如果需要詳細瞭解,推薦跳轉到那篇哦。

Paxos 算法是一個名字叫 Lesile Lamport 提出的一種基於消息傳遞且具有高度容錯特性的一致性算法

是不是覺得繞口?沒事,我們只需要知道,分佈式系統中不可避免的會發生進程被 kill,消息延遲,重複,丟失 ··· 一系列問題,Paxos 算法就是在這些異常情況下的仍然保證數據一致性的東西。那這東西和 Zookeeper 有啥關係呢?Zookeeper 是存在一個 ZAB 協議的,但是這個 ZAB 協議底層就是封裝了 Paxos 算法的。

3.2 Paxos 中存在的角色及與 Zookeeper 集羣的關係

Proposer 提議者:顧名思義就是發起提案的人

Acceptor 接受者:它們是可以表決的,可以接受或者否決提案

Learner 學習者:提案被超過半數的 Acceptor 接受的話,就學習這個提案

映射到 Zookeeper 集羣中,就分別是 leader,follower,observer,它們有點像是主席,人大代表,和全國老百姓的關係,主席提出一個提案,人大代表參與投票,全國老百姓被動接受😂,大概就是這麼個感覺。相比於之前的 2PC,3PC,它只需要半數通過即可提交。所以這種屬於弱一致性,2PC,3PC 這些就屬於強一致性

3.3 Raft 算法

請點擊這個鏈接,相信你一定能夠很快掌握。http://thesecretlivesofdata.com/raft/ 我這裏還是小小的說明一下吧,這個是一個 PPT 的形式,告訴你,Raft 到底是個什麼東西,非常好懂,我這裏跳過前面的一些東西,直奔主題

這裏說到了,Raft 是實現分佈式共識算法的一個協議

這裏假設一個節點有 3 種不同的狀態

第一種,follower state(無線條)

第二種,candidate state(虛線)

第三種,leader state(實線) 記住 leader 是從 candidate 候選人那裏選出來的

首先我們一上來,所有的節點都是 follower state

接下來,所有的 follower 節點都尋找 leader ,當他們找不到的時候,就會自發成爲候選人發起投票(問其它人是否贊成我成爲 leader),什麼情況纔會找不到呢?那肯定就是 leader 掛了嘛😂

此時它就發送給其它節點投票的提案,然後其它節點也會給予它反饋,當它接收到超過半數的節點的反饋的時候,它就可以順理成章的成爲 leader 了。

之後寫數據的請求就會直接發給 leader,由 leader 廣播給其它的 follower,此時也是隻要超過半數節點返回正反饋,那這個寫數據的事務就會被執行,然後 leader 再給它們發送提交命令,事務就算執行成功了。

3.4 ZAB 協議

內容在 從零開始的高併發(四)--- Zookeeper 的分佈式隊列

Zookeeper 的底層實現就是 ZAB 協議,它實現了崩潰恢復(leader 崩潰)和消息廣播(客戶端寫數據 Zookeeper 要保證多節點都成功寫入)功能。主要就是保證在 leader 服務器上提交的事務最終讓所有服務器都提交,並確保丟棄掉只在 leader 服務器上所提出的事務

3.5 Quorum NWR 機制

Quorum NWR:Quorum 機制是分佈式場景中常用的,用來保證數據安全,並且在分佈式環境中實現最終一致性的投票算法。這種算法的主要原理來源於鴿巢原理。它最大的優勢,既能實現強一致性,而且還能自定義一致性級別。

鴿巢原理,又名狄利克雷抽屜原理、鴿籠原理。

其中一種簡單的表述法爲:

若有 n 個籠子和 n+1 只鴿子,所有的鴿子都被關在鴿籠裏,那麼至少有一個籠子有至少 2 只鴿子。

另一種爲:若有 n 個籠子和 kn+1 只鴿子,所有的鴿子都被關在鴿籠裏,那麼至少有一個籠子有至少 k+1 只鴿子。

爲什麼從抽屜原理說起?一來大家對這個比較熟悉,也容易理解,二來它與 Quorum 機制有異曲同工的地方。抽屜原理,2 個抽屜每個抽屜最多容納 2 個蘋果,現在有 3 個蘋果無論怎麼放,其中的一個抽屜裏面肯定會有 2 個蘋果。那麼我們把抽屜原理變變型,2 個抽屜一個放了 2 個紅蘋果,另一個放了 2 個青蘋果,我們取出 3 個蘋果,無論怎麼取至少有 1 個是紅蘋果,這個理解起來也很簡單。我們把紅蘋果看成更新了的有效數據,青蘋果看成未更新的無效數據。便可以看出來,不需要更新全部數據(並非全部是紅蘋果)我們就可以得到有效數據,當然我們需要讀取多個副本(取出多個蘋果)。

回到 Quorum NWR 機制 的 NWR 到底指什麼

N:複製的節點數,即一份數據被保存的副本數。

W:寫操作成功的節點數,即每次數據寫入寫成功的副本數。W 肯定是小於等於 N 的。

R:讀操作獲取最新版本數據所需的最小節點數,即每次讀取成功至少需要讀取的副本數。

總結:這三個因素決定了可用性,一致性分區容錯性。只要保證(W + R > N)就一定能讀取到最新的數據,數據一致性級別完全可以根據讀寫副本數的約束來達到強一致性!

分以下三種情況討論:前提,當 N 已經固定了。

W = 1, R = N,Write Once Read All

在分佈式環境中,寫一份,那麼如果要讀取到最新數據,就必須要讀取所有節點,然後取最新版本的值了。寫操作高效,但是讀操作效率低。一致性高,分區容錯性差,可用性低

R = 1, W = N, Read Only Write All

在分佈式環境中,所有節點都同步完畢,才能讀取,所以只要讀取任意一個節點就可以讀取到最新數據。讀操作高效,但是寫操作效率低。分區容錯性好,一致性差,實現難度更高,可用性高

W = Q, R = Q where Q = N/2 + 1

可以簡單理解爲寫超過一半節點,那麼讀也超過一半節點,取得讀寫性能平衡。一般應用適用,讀寫性能之間取得平衡。如 N=3, W=2, R=2,分區容錯性,可用性,一致性取得一個平衡。而 ZooKeeper 就是這麼去設計的

需要補充的是,Zookeeper 並沒有實現必須要客戶端讀取超過半數的節點,所以它是允許客戶端讀取到的不是最新同步完成的數據的,但是可能性比較小。數據沒有同步完成的節點其實客戶端也大概率是連接不上的,因爲無論是網絡問題還是機器問題導致 leader 發送數據過去它做不了的話,客戶端肯定也連不上它。要是剛好就是在同步數據的中間狀態客戶端發起了訪問的話,也是有辦法解決的,可以自己瞭解一下。

3.6 CAP 理論

CAP 理論:2000 年 7 月份被首次提出,CAP 理論告訴我們,一個分佈式系統不可能同時滿足 C,A,P 三個需求

C:Consistency,強一致性,分佈式環境中多個數據副本保持一致

A:Availability,高可用性,系統提供的服務必須一直處於可用,對於用戶的每一個操作請求總是能在有限時間內返回結果

P:Partition Tolerance 分區容錯性,分佈式系統在遇到任何網絡分區故障時,仍然需要能夠保證對外提供滿足一致性和可用性的服務

既然一個分佈式系統不能同時滿足 C,A,P 三個需求,我們就要就我們的需求去取捨了。

放棄 P:最簡單的極端做法,就是放置在一個節點上,也就只有一個數據副本,所有讀寫操作就集中在一臺服務器上,有單點故障問題。放棄 P 也就意味着放棄了系統的可擴展性,所以分佈式系統一般來說,都會保證 P

放棄 A:一旦系統遇到網絡分區或者其他故障時,服務需要等待一段時間,在等待時間內就無法正常對外提供服務,即服務不可用

放棄 C:事實上,放棄一致性是指放棄數據的強一致性,而保留最終一致性,具體多久達到數據同步取決於存儲系統的設計

CAP 只能 3 選 2,因爲在分佈式系統中,容錯性 P 肯定是必須有的,所以這時候無非就兩種情況,網絡問題導致要麼錯誤返回,要麼阻塞等待,前者犧牲了一致性,後者犧牲了可用性。舉個例子,比如 HBase 就是追求數據的一致性的,而 Cassandra 則是可用性。

經驗總結:

1、不要花費精力浪費在設計同時滿足CAP的分佈式系統
2、分區容錯性往往是分佈式系統必然要面對和解決的問題。所以應該把精力放在如何根據業務特點在A和C之間尋求平衡。
3、對於單機軟件,因爲不用考慮P,所以肯定是 CA 型,比如 MySQL
4、對於分佈式軟件,因爲一定會考慮P,所以又不能兼顧A和C的情況下,只能在A和C做權衡,
比如 HBase, Redis 等。做到服務基本可用,並且數據最終一致即可。

所以,就產生了 BASE 理論。

3.7 BASE 理論

多數情況下,其實我們也並非一定要求強一致性,部分業務可以容忍一定程度的延遲一致,所以爲了兼顧效率,發展出來了最終一致性理論 BASE,來自 ebay 的架構師提出。BASE 理論全稱:全稱:Basically Available(基本可用),Soft state(軟狀態), 和 Eventually consistent(最終一致性)三個 短語的縮寫。核心思想是:即使無法做到強一致性,但每個應用都可 以根據自身業務特點,採用適當的方式來使系統達到最終一致性。一句話概括,做事別走極端,BASE 是對 CAP 理論中的 C 和 A 進行權衡得到的結果。

不是強一致,而是最終一致。不是高可用,而是基本可用。

Basically Available(基本可用):基本可用是指分佈式系統在出現故障的時候,允許損失部分可用性,即保證核心可用

例如淘寶雙 11,爲保護系統穩定性,正常下單,其他邊緣服務可暫時不可用。部分非核心服務宕機此時是允許的。

Soft State(軟狀態):軟狀態是指允許系統存在中間狀態,而該中間狀態不會影響系統整體可用性。分佈式存儲中一般一份數據至少會有三個副本,允許不同節點間副本同步的延時就是軟狀態的體現。通俗的講:允許存在不同節點同步數據時出現延遲,且出現數據同步延遲時存在的中間狀態也不會影響系統的整體性能

Eventually Consistent(最終一致):最終一致性是指系統中的所有數據副本經過一定時間後,最終能夠達到一致的狀態。弱一致性和強一致性相反,最終一致性是弱一致性的一種特殊情況,要求最終達到一致,而不是實時強一致

Finally

總的來說,我們提到了集中式 和 分佈式服務部署架構的分析,設計分佈式系統會遇到的各種難題:數據一致性的問題

2PC 和 3PC 是通用的思路實現,還是有缺點。Paxos Raft ZAB 就算出現了分佈式網絡通信異常等相關棘手的問題,以上這些算法也能實現一致性

議會制 Quorum NWR 機制:R + W > N ===> 少數服從多數

一致性 和 可用性的衝突問題,CAP 和 BASE:分佈式系統一定要滿足 P,只能在 C 和 A 中做權衡

絕大部分系統都是 BASE 系統(基本可用 + 最終一致)

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/OMHbwNhzA11cinS5H8MVkw