帶你瞭解分散式系統的資料一致性問題

努力的老劉發表於2021-01-10

老劉是一名即將找工作的研二學生,寫部落格一方面是複習總結大資料開發的知識點,一方面是希望能夠幫助和自己一樣自學程式設計的夥伴。由於老劉是自學大資料開發,部落格中肯定會存在一些不足,還希望大家能夠批評指正,讓我們一起進步!


今天給各位小夥伴聊聊分散式系統的資料一致性問題,這個一定要從伺服器架構部署的發展歷程講起!文章篇幅較長,請大家耐心觀看,精彩千萬不要錯過!

1. 背景

1.1. 集中式服務

首先要講的是集中式服務,那集中式是什麼?就是事情都由一臺伺服器搞定。

而集中式系統就是由一臺或多臺主計算機組成中心節點,資料集中儲存於這個中心節點中,並且整個系統的所有業務都在這個中心節點上,系統所有的功能都由它做。

也就是說,在集中式系統中,每個客戶端僅僅負責資料的輸入和輸出,而資料的儲存與控制處理完全交給主機完成。

那集中式服務優點:

  1. 結構簡單
  2. 部署簡單
  3. 專案架構簡單

但是它的缺點也是非常明顯:

  1. 大型主機的研發和維護成本非常高
  2. 大型主機非常昂貴
  3. 存在單點故障問題,主機一掛,所有服務終止
  4. 大型主機的效能擴充套件受限於摩爾定律

什麼是摩爾定律?

摩爾定律是由英特爾(Intel)創始人之一戈登·摩爾(Gordon Moore)提出來的。其內容為:當價格不變時,積體電路上可容納的元器件的數目,約每隔18-24個月便會增加一倍,效能也將提升一倍。換言之,每一美元所能買到的電腦效能,將每隔18-24個月翻一倍以上。摘自:百度百科

摩爾定律告訴我們:縱向擴充套件理論上是受限的,所以只能考慮橫向擴充套件,而且從理論上說,橫向擴充套件理論上是不受限的!

那既然縱向擴充套件受限,我們就去嘗試橫向擴充套件,就有了分散式!

1.2. 分散式服務

分散式意味著可以採用更多的普通計算機(相對於昂貴的大型機)組成分散式叢集對外提供服務。計算機越多,CPU、記憶體、儲存資源等也就越多,能夠處理的併發訪問量也就越大。

例如一個由分散式系統實現的電子商城,在功能上可能被拆分成多個應用,分別提供不同的功能,組成一個分散式系統對外提供服務。

所以,分散式系統中的計算機在空間上是幾乎沒有限制的,這些計算機可能被放在不同的機櫃上,也可能被部署在不同的機房中,還可能在不同的城市中。

和集中式系統相比,分散式系統的價效比更高、處理能力更強、可靠性更高、也有很好的擴充套件性。

但是,分散式解決了網站的高併發問題的同時也帶來了一些其他問題。

首先,分散式的必要條件就是網路,這可能對效能甚至服務能力造成一定的影響。其次,一個叢集中的伺服器數量越多,伺服器當機的概率也就越大。另外,由於服務在叢集中分散式部署,使用者的請求只會落到其中一臺機器上,所以,一旦處理不好就很容易產生資料一致性問題。

1.3. 分散式存在的異常

1、通訊異常:網路不可用(訊息延遲或者丟失),會導致分散式系統內部無法順利進行網路通訊,所以可能造成多個節點資料丟失和狀態不一致,還有可能造成資料亂序。

2、網路分割槽:網路不連通,但各個子網路的內部網路是正常的,從而導致整個系統的網路環境被切分成若干個孤立的區域,分散式系統就出現了區域性小叢集造成的資料不一致。

3、節點故障:伺服器節點出現的當機的現象。

4、儲存資料丟失:對於有狀態節點來說,資料丟失意味著狀態丟失,通常只能從其他節點讀取、恢復儲存的狀態。解決方案:利用多副本機制。

1.4. 衡量分散式系統的效能指標

1、效能:這是一個非常讓人頭疼的問題,追求高吞吐的系統,往往很難做到低延遲;系統平均響應時間較長時,也很難提高QPS。

系統的吞吐能力,指系統在某一時間可以處理的資料總量,通常可以用系統每秒處理的總資料量來衡量; 
系統的響應延遲,指系統完成某一功能需要使用的時間; 
系統的併發能力,指系統可以同時完成某一功能的能力,通常也用QPS來衡量。

2、可用性:系統的可用性(availability)指系統在面對各種異常時可以正確提供服務的能力。可用性是分散式的重要指標,衡量了系統的魯棒性,是系統容錯能力的體現。

3、可擴充套件性:系統的可擴充套件性(scalability)指分散式系統通過擴充套件叢集機器規模提高系統效能(吞吐、延遲、併發)、儲存容量、計算能力的特性。

4、一致性:分散式系統為了提高可用性,總是不可避免地使用副本的機制,從而引發副本一致性的問題。

例如,就是一份資料存在分散式系統,存在多個不同的節點當中存著相同的資料。如果多個不同的節點存的資料不一樣,多個客戶端去訪問的時候就會存在這種情況,第1個客戶端去訪問的結果為A,第2個客戶端訪問的結果為B,兩個客戶端訪問得到不同的結果,那就是一致性做的不好。

說了這麼多,我們如果設計一個優秀的分散式系統,它應該具有這些特點:吞吐高、響應延遲低、併發強、可用性很高、可擴充套件性很強、一致性很好。但並不是每個特點都能滿足,有幾個特點是相互矛盾的,需要我們想辦法克服!

而在分散式場景中真正複雜的是資料一致性的問題!

1.5. 一致性理解

一致性也分很多種,這裡說說老劉瞭解的三個。

強一致性:寫操作完成之後,讀操作一定能讀到最新資料。通俗地講就是客戶端只要把結果寫進去了,什麼時候訪問都能拿到最新的資料。但是在分散式場景中很難實現,後續的Paxos 演算法,Quorum 機制,ZAB 協議等能實現!

弱一致性:不保證拿到最新的資料,也有可能拿到舊的資料。

最終一致性:不考慮中間的任何狀態,只保證經過一段時間之後,最終系統內資料正確。在高併發場景中,它也是使用最廣的一致性模型。

1.6. 分散式一致性的作用

說了那麼多分散式一致性的內容,那它的作用是什麼呢?

1、為了提高系統的可用性,一般都會使用多副本機制,多副本就會有分散式一致性的問題,它就是為了提高系統的可用性,防止單點節點故障引起的系統不可用。

2、提高系統的整體效能,資料分佈在叢集中多個節點上,它們都能為使用者提供服務。

老劉說了這麼多,大家有沒有猜到想引出什麼內容呢?

上述那麼多內容只為引出分散式系統的資料一致性問題!我們用來解決分散式系統的資料一致性問題的方案有如下:

分散式事務+事務 
分散式一致性演算法 
Quorum機制 
CAP和BASE理論

2. 分散式事務

分散式系統中,每個節點都能知道自己的事務操作是否成功,但是沒法知道系統中的其他節點的事務是否成功。這就有可能會造成分散式系統中的各節點的狀態出現不一致。因此當一個事務需要跨越伺服器節點,並且要保證事務的ACID特性時,就必須引入一個協調者的角色。那麼其他的各個進行事務操作的節點就都叫做參與者。

現實生活中有兩種典型的分散式事務的提交模式:2PC和3PC。

2.1. 2PC提交過程

直接上圖:

我讓A去做一件事,讓B去做另外一件事,並且這兩件事在一個分散式事務中要保證同時成功或失敗。那如何做到資料一致呢?

2PC分兩個階段: 

第一階段:執行事務,但不提交。

第二階段:當協調者收到第一階段中所有事務參與者的正反饋時(事務都執行成功了),

就去發命令讓所有參與者提交事務。

 

2.2. 2PC的問題

看了2PC的兩個提交階段和圖,有經驗的人一眼就會看出裡面存在的問題。

1 阻塞問題

協調者傳送命令給參與者,由於是網路傳送命令,就會存在不同參與者收到的命令有先後、有延遲。例如參與者A很快就收到了,參與者B網路有問題,過了很久才收到命令。參與者A很快處理完髮送反饋,
而參與者B就很久之後才傳送反饋,導致協調者等待時間特別長。

這就是一個非常典型的阻塞問題,非常浪費資源,影響效能!

2 沒有容錯機制,存在單點故障問題

事務協調者是整個分散式事務的核心,一旦協調者出現故障,看看上面那張圖,就會知道參與者就收不到
commit/rollback的通知,從而導致參與者節點一直處於事務無法完成的中間狀態。

3 資料不一致

在第二階段,如果發生區域性網路問題,一個參與者收到提交的命令,另一個參與者沒有收到提交的命令,
就會造成節點間資料不一致。

2.3. 3PC

3PC就是三階段提交的意思,它是2階段提交的改進版,把二階段提交協議的 "提交事務請求" 一分為二,形成了cancommit,precommit,docommit 三個階段。

除了在 2PC 的基礎上增加了CanCommit階段,還引入了超時機制。一旦事務參與者在指定時間內沒有收到協調者的 commit/rollback 指令,就會自動本地 commit,這樣可以解決協調者單點故障的問題。

2.4. 執行過程解析

第一階段:CanCommit階段

在第一階段準備的時候,先問一下各個參與者是否可以進行事務操作以及超時機制,參與者在一定時間沒
收到協調者的指令會自動提交。

第二階段:PreCommit階段

1、如果每個參與者返回的都是同意,協調者則向所有參與者傳送預提交請求,並進入預提交階段; 

2、參與者收到預提交請求後,執行事務操作。

3、參與者執行完本地事務之後,會向協調者發出Ack表示已準備好提交,並等待協調者下一步指令。

4、如果協調者收到預提交響應為拒絕或者超時,則執行中斷事務操作,通知各參與者中斷事務。 

5、參與者收到中斷事務或者等待超時,都會主動中斷事務/直接提交

第三階段:doCommit階段

1、協調者收到所有參與 的Ack,則從預提交入提交段,並向各參與者傳送提交請求。 

2、參與者收到提交請求,正式提交事務(commit),並向協調者反饋提交結果Y/N。

3、協調者收到所有反饋訊息,完成分散式事務。

4、如果協調者超時沒有收到反饋,則傳送中斷事務指令。

5、參與者收到中斷事務指令後,利用事務日誌進行rollback。 

6、參與者反饋回滾結果,協調者接收反饋結果或者超時,完成中斷事務。

2.5. 3PC的問題

3PC也可能出現資料不一致,第三階段讓所有參與者回滾事務,但有一個參與者在規定的時間內沒有收到,它會預設進行提交操作,就會出現資料不一致。由於網路問題,第二階段到第三階段之間特別容易出現資料不一致問題。

3. 分散式一致性演算法

在2PC和3PC的原理上,優秀的開發者們實現了分散式一致性演算法,這裡老劉先大致講講Poxos演算法和ZAB協議的相關概念。如果想詳細瞭解Paxos演算法和ZAB協議,等老劉找完工作後,專門寫一篇Zookeeper原始碼文章。

3.1. Paxos演算法

Paxos 演算法使用一個希臘故事來描述,在 Paxos 中,存在三種角色,分別為

1、Proposer(提議者,用來發出提案proposal),

2、Acceptor(接受者,可以接受或拒絕提案),

3、Learner(學習者,學習被選定的提案,當提案被超過半數的Acceptor接受後為被批准)。

對映到 zookeeper 叢集:

leader:發起提案  主席(單點故障的解決辦法是leader選舉機制)

follower:參與投票  人大代表

observer:被動接受  全國所有人

以及有一個特別出名的機制:議會制

保證超過半數達成一致性即可的協議

總結下Paxos演算法,它就是所有事務請求必須由一個全域性唯一的伺服器來協調處理,這樣的伺服器被稱為 leader 伺服器,而餘下的其他伺服器則成為 follower 伺服器。

leader 伺服器負責將一個客戶端事務請求轉換成一個事務proposal,並將該 proposal 分發給叢集中所有的follower 伺服器。之後 leader 伺服器需要等待所有follower 伺服器的反饋,一旦超過半數的 follower 伺服器進行了正確的反饋後,那麼 leader 就會再次向所有的 follower 伺服器分發 commit 訊息,要求其將前一個 proposal 進行提交。

3.2. ZAB協議

ZooKeeper的底層工作機制,就是依靠 ZAB 實現的。它實現了崩潰回覆和訊息廣播兩個主要功能。

ZAB協議保證資料一致性的兩個重要特點就是:

1、ZAB協議需要確保那些已經在 leader 伺服器上提交的事務最終被所有伺服器都提交。

2、ZAB協議需要確保丟棄那些只在 leader 伺服器上被提出的事務。

為了解決單點故障,有leader選舉演算法。在leader選舉中,如果讓 leader 選舉演算法能夠保證新選舉出來的 leader 伺服器擁有叢集中所有機器最高事務編號(ZXID)的事務proposal,那麼就可以保證這個新選舉出來的 leader 一定具有所有已經提交的提案。

因為事務的每次執行都會有一個編號,最高事務編號代表著最新的事務,即最新的資料。 根據上述ZAB協議內容,ZooKeeper實現了分散式系統資料的一致性!

4. 鴿巢原理

簡單描述:若有n個籠子和n+1只鴿子,所有的鴿子都被關在鴿籠裡,那麼至少有一個籠子有至少2只鴿子 。

5. Quorum NWR機制

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

N:總節點數

W:總寫入成功數

R:總讀取數

當W+R>N時,一定能保證讀到最新的資料,即強一致性! 為什麼這樣說?

如上圖,有4個箱子,3個箱子裡面有東西,那如何保證一定能拿到有資料的箱子?最起碼拿2個箱子就能拿到有東西的箱子!

就是利用這種原理,只要保證(W + R > N)就一定能讀取到最新的資料,資料一致性級別完全可以根據讀寫副本數的約束來達到強一致性!

那現在分以下三種情況討論:前提是N已經確定不改了!

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

在分散式環境中,寫一份,相當於只有只有一個箱子有東西,那麼如果要讀取到最新資料,即拿到有東西的箱子,就必須要讀取所有節點,然後取最新版本的值了。寫操作高效,但是讀操作效率低。一致性高,但分割槽容錯性差,可用性低。

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

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

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

可以簡單理解為寫超過一半節點,那麼讀也超過一半節點,取得讀寫效能平衡。一般應用適用,讀寫效能之間取得平衡。如 N=3, W=2, R=2,分割槽容錯性,可用性,一致性取得一個平衡。

ZooKeeper就是這麼幹的!採用了第三種情況!

6. CAP理論

根據上述說的,做到強一致性了,就難做到高可用,兩者是非常矛盾的。所以CAP理論就告訴我們,一個分散式系統不可能同時滿足C,A,P三個需求。

C:Consistency,強一致性

分散式環境中多個資料副本保持一致

A:Availability,高可用性

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

P:Partiton Tolerance 分割槽容錯性

分散式系統在遇到任何網路分割槽故障時,仍然需要能夠保證對外提供滿足一致性和可用性的服務

既然一個分散式系統不能同時滿足C,A,P三個需求,那麼如何選擇?

CAP只能3選2,因為在分散式系統中,容錯性P肯定是必須有的,所以這時候無非就兩種情況,網路問題導致要麼錯誤返回,要麼阻塞等待,前者犧牲了一致性,後者犧牲了可用性。

對於單機軟體,因為不同考慮P,所以肯定是CA型,比如MySQL。

對於分散式軟體,因為一定會考慮P,所以又不能兼顧A和C的情況下,只能在A和C做權衡,比如HBase、Redis等。做到服務基本可用,並且資料最終一致性即可。 所以,就產生了BASE理論。

7. BASE理論

多數情況下,其實我們也並非一定要求強一致性,部分業務可以容忍一定程度的延遲一致,所以為了兼顧效率,發展出來了最終一致性理論 BASE,它的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,採用適當的方式來使系統達到最終一致性。

一句話就是做事別走極端,BASE 是對 CAP 理論中的 C 和 A 進行權衡得到的結果。

BASE理論做到的不是強一致,而是最終一致;不是高可用,而是基本可用。

Basically Available(基本可用):基本可用是指分散式系統在出現故障的時候,允許損失部分可用性,保證核心可用。 例如:淘寶雙11,為保護系統穩定性,正常下單,其他邊緣服務可暫時不可用。

Eventually Consistent(最終一致):最終一致性是指系統中的所有資料副本經過一定時間後,最終能夠達到一致的狀態。

以後開發分散式系統,就可以根據業務來決定到底追求高可用還是追求強一致性!

8. 總結

好啦,分散式系統的資料一致性問題大致聊得差不多了,老劉主要給大家講了講分散式系統一致性的背景以及實現。儘管當前水平可能不及各位大佬,但老劉還是希望能夠變得更加優秀,能夠幫助更多自學程式設計的夥伴。

如果有相關問題,請聯絡公眾號:努力的老劉,和老劉進行愉快的交流,如果覺得幫到了您,不妨點贊關注支援一波!

 

相關文章