據說60%的Java程式設計師不明白分散式一致性?這次徹底搞懂!

  • 2019 年 10 月 5 日
  • 筆記

前言

在電腦科學領域,分散式一致性是一個相當重要且被廣泛探索與論證問題,首先來看三種業務場景。

1、火車站售票

假如說我們的終端用戶是一位經常坐火車的旅行家,通常他是去車站的售票處購買車票,然後拿著車票去檢票口,再坐上火車,開始一段美好的旅行—-一切似乎都是那麼和諧。

想像一下,如果他選擇的目的地是杭州,而某一趟開往杭州的火車只剩下最後一張車票,可能在同一時刻,不同售票窗口的另一位乘客也購買了同一張車票。假如說售票系統沒有進行一致性的保障,兩人都購票成功了。而在檢票口檢票的時候,其中一位乘客會被告知他的車票無效……

當然,現代的中國鐵路售票系統已經很少出現這樣的問題了。但在這個例子中我們可以看出,終端用戶對於系統的需求非常簡單:

"請售票給我,如果沒有餘票了,請在售票的時候就告訴我票是無效的"

這就對購票系統提出了嚴格的一致性要求—-系統的數據(本例中指的就是那趟開往杭州的火車的余票數)無論在哪個售票窗口,每時每刻都必須是準確無誤的!

2、銀行轉賬

假如我們的終端用戶是一位剛畢業的大學生,通常在拿到第一個月工資的時候,都會選擇向家裡匯款。當他來到銀行櫃檯,完成轉賬操作後,銀行的櫃檯服務員會友善地提醒他:"您的轉賬將在N個工作日後到賬!"。

此時這名畢業生有一定的沮喪,會對那名櫃檯服務員叮囑:"好吧,多久沒關係,錢不要少就好了!"……

這也成為了幾乎所有用戶對於現代銀行系統最基本的需求

3、網上購物

假如說我們的終端用戶是一位網購達人,當他看見一件庫存量為5的心儀商品,會迅速地確認購買,寫下收貨地址,然後下單……

然而,在下單的那個瞬間,系統可能會告知該用戶:"庫存量不足!"。此時絕大部分消費者都會抱怨自己動作太慢,使得心愛的商品被其他人搶走了。

但其實有過網購系統開發經驗的工程師一定明白,在商品詳情頁上顯示的那個庫存量,通常不是該商品的真實庫存量,只有在真正下單購買的時候,系統才會檢查該商品的真實庫存量。但是,誰在意呢?

問題的解讀

對於上面三個例子,相信大家一定看出來了,我們的終端用戶在使用不同的電腦產品時對於數據一致性的需求是不一樣的:

1、有些系統,既要快速地響應用戶,同時還要保證系統的數據對於任意客戶端都是真實可靠的,就像火車站售票系統

2、有些系統,需要為用戶保證絕對可靠的數據安全,雖然在數據一致性上存在延時,但最終務必保證嚴格的一致性,就像銀行的轉賬系統

3、有些系統,雖然向用戶展示了一些可以說是"錯誤"的數據,但是在整個系統使用過程中,一定會在某一個流程上對系統數據進行準確無誤的檢查,從而避免用戶發生不必要的損失,就像網購系統

分散式一致性的提出

在分散式系統中要解決的一個重要問題就是數據的複製。

在我們的日常開發經驗中,相信很多開發人員都遇到過這樣的問題:假設客戶端C1將系統中的一個值K由V1更新為V2,但客戶端C2無法立即讀取到K的最新值,需要在一段時間之後才能讀取到。

這很正常,因為資料庫複製之間存在延時。

分散式系統對於數據的複製需求一般都來自於以下兩個原因:

1、為了增加系統的可用性,以防止單點故障引起的系統不可用

2、提高系統的整體性能,通過負載均衡技術,能夠讓分布在不同地方的數據副本都能夠為用戶提供服務

數據複製在可用性和性能方面給分散式系統帶來的巨大好處是不言而喻的,然而數據複製所帶來的一致性挑戰,也是每一個系統研發人員不得不面對的。

所謂分布一致性問題,是指在分散式環境中引入數據複製機制之後,不同數據節點之間可能出現的,並無法依靠電腦應用程式自身解決的數據不一致的情況。簡單講,數據一致性就是指在對一個副本數據進行更新的時候,必須確保也能夠更新其他的副本,否則不同副本之間的數據將不一致。

那麼如何解決這個問題?一種思路是"既然是由於延時動作引起的問題,那我可以將寫入的動作阻塞,直到數據複製完成後,才完成寫入動作"。

沒錯,這似乎能解決問題,而且有一些系統的架構也確實直接使用了這個思路。但這個思路在解決一致性問題的同時,又帶來了新的問題:寫入的性能。

如果你的應用場景有非常多的寫請求,那麼使用這個思路之後,後續的寫請求都將會阻塞在前一個請求的寫操作上,導致系統整體性能急劇下降。

總得來說,我們無法找到一種能夠滿足分散式系統所有系統屬性的分散式一致性解決方案。因此,如何既保證數據的一致性,同時又不影響系統運行的性能,是每一個分散式系統都需要重點考慮和權衡的。於是,一致性級別由此誕生:

1、強一致性

這種一致性級別是最符合用戶直覺的,它要求系統寫入什麼,讀出來的也會是什麼,用戶體驗好,但實現起來往往對系統的性能影響大

2、弱一致性

這種一致性級別約束了系統在寫入成功後,不承諾立即可以讀到寫入的值,也不久承諾多久之後數據能夠達到一致,但會儘可能地保證到某個時間級別(比如秒級別)後,數據能夠達到一致狀態

3、最終一致性

最終一致性是弱一致性的一個特例,系統會保證在一定時間內,能夠達到一個數據一致的狀態。這裡之所以將最終一致性單獨提出來,是因為它是弱一致性中非常推崇的一種一致性模型,也是業界在大型分散式系統的數據一致性上比較推崇的模型 歡迎大家關注我的公種浩【程式設計師追風】,文章都會在裡面更新,整理的資料也會放在裡面。

分散式環境的各種問題

分散式系統體系結構從其出現之初就伴隨著諸多的難題和挑戰:

1、通訊異常

從集中式向分散式演變的過程中,必然引入網路因素,由於網路本身的不可靠性,因此也引入了額外的問題。

分散式系統需要在各個節點之間進行網路通訊,因此每次網路通訊都會伴隨著網路不可用的風險,網路光纖、路由器或是DNS等硬體設備或是系統不可用都會導致最終分散式系統無法順利完成一次網路通訊。

另外,即使分散式系統各個節點之間的網路通訊能夠正常進行,其延時也會大於單機操作。

通常我們認為現代電腦體系結構中,單機記憶體訪問的延時在納秒數量級(通常是10ns),而正常的一次網路通訊的延遲在0.1~1ms左右(相當於記憶體訪問延時的105倍),如此巨大的延時差別,也會影響到消息的收發過程,因此消息丟失和消息延遲變得非常普遍。

2、網路分區

當網路由於發生異常情況,導致分散式系統中部分節點之間的網路延時不斷增大,最終導致組成分散式系統的所有節點中,只有部分節點之間能夠正常通訊,而另一些節點則不能—-我們將這個現象稱為網路分區。

當網路分區出現時,分散式系統會出現局部小集群,在極端情況下,這些局部小集群會獨立完成原本需要整個分散式系統才能完成的功能,包括對數據的事物處理,這就對分散式一致性提出了非常大的挑戰。

3、三態

上面兩點,我們已經了解到在分散式環境下,網路可能會出現各式各樣的問題,因此分散式系統的每一次請求與響應,存在特有的三態概念,即成功、失敗、超時

在傳統的單機系統中,應用程式在調用一個函數之後,能夠得到一個非常明確的響應:成功或失敗。而在分散式系統中,由於網路是不可靠的,雖然在絕大部分情況下,網路通訊也能夠接受到成功或失敗的響應,當時當網路出現異常的情況下,就可能會出現超時現象,通常有以下兩種情況:

(1)由於網路原因,該請求並沒有被成功地發送到接收方,而是在發送過程中就發生了消息丟失現象

(2)該請求成功地被接收方接收後,進行了處理,但是在將響應回饋給發送方的過程中,發生了消息丟失現象

當出現這樣的超時現象時,網路通訊的發起方是無法確定當前請求是否被成功處理的

4、節點故障

節點故障則是分散式環境下另一個比較常見的問題,指的是組成分散式系統的伺服器節點出現的宕機或"僵死"現象,通常根據經驗來說,每個節點都有可能出現故障,並且每天都在發生

分散式事務

隨著分散式計算的發展,事物在分散式計算領域也得到了廣泛的應用。

在單機資料庫中,我們很容易能夠實現一套滿足ACID特性的事物處理系統,但在分散式資料庫中,數據分散在各台不同的機器上,如何對這些數據進行分散式的事物處理具有非常大的挑戰。

分散式事物是指事物的參與者、支援事物的伺服器、資源伺服器以及事物管理器分別位於分散式系統的不同節點上,通常一個分散式事物中會涉及對多個數據源或業務系統的操作。

可以設想一個最典型的分散式事物場景:一個跨銀行的轉賬操作涉及調用兩個異地的銀行服務,其中一個是本地銀行提供的取款服務,另一個則是目標銀行提供的存款服務,這兩個服務本身是無狀態並且相互獨立的,共同構成了一個完整的分散式事物。

如果從本地銀行取款成功,但是因為某種原因存款服務失敗了,那麼就必須回滾到取款之前的狀態,否則用戶可能會發現自己的錢不翼而飛了。

從這個例子可以看到,一個分散式事務可以看做是多個分散式的操作序列組成的,例如上面例子的取款服務和存款服務,通常可以把這一系列分散式的操作序列稱為子事物。

因此,分散式事務也可以被定義為一種嵌套型的事物,同時也就具有了ACID事物特性。但由於在分散式事務中,各個子事物的執行是分散式的,因此要實現一種能夠保證ACID特性的分散式事物處理系統就顯得格外複雜。

CAP理論

一個經典的分散式系統理論。CAP理論告訴我們:一個分散式系統不可能同時滿足一致性(C:Consistency)、可用性(A:Availability)和分區容錯性(P:Partition tolerance)這三個基本需求,最多只能同時滿足其中兩項

1、一致性

在分散式環境下,一致性是指數據在多個副本之間能否保持一致的特性。在一致性的需求下,當一個系統在數據一致的狀態下執行更新操作後,應該保證系統的數據仍然處於一直的狀態。

對於一個將數據副本分布在不同分散式節點上的系統來說,如果對第一個節點的數據進行了更新操作並且更新成功後,卻沒有使得第二個節點上的數據得到相應的更新,於是在對第二個節點的數據進行讀取操作時,獲取的依然是老數據(或稱為臟數據),這就是典型的分散式數據不一致的情況。

在分散式系統中,如果能夠做到針對一個數據項的更新操作執行成功後,所有的用戶都可以讀取到其最新的值,那麼這樣的系統就被認為具有強一致性

2、可用性

可用性是指系統提供的服務必須一直處於可用的狀態,對於用戶的每一個操作請求總是能夠在有限的時間內返回結果。這裡的重點是"有限時間內"和"返回結果"。

"有限時間內"是指,對於用戶的一個操作請求,系統必須能夠在指定的時間內返回對應的處理結果,如果超過了這個時間範圍,那麼系統就被認為是不可用的。

另外,"有限的時間內"是指系統設計之初就設計好的運行指標,通常不同系統之間有很大的不同,無論如何,對於用戶請求,系統必須存在一個合理的響應時間,否則用戶便會對系統感到失望。

"返回結果"是可用性的另一個非常重要的指標,它要求系統在完成對用戶請求的處理後,返回一個正常的響應結果。正常的響應結果通常能夠明確地反映出隊請求的處理結果,即成功或失敗,而不是一個讓用戶感到困惑的返回結果。

3、分區容錯性

分區容錯性約束了一個分散式系統具有如下特性:分散式系統在遇到任何網路分區故障的時候,仍然需要能夠保證對外提供滿足一致性和可用性的服務,除非是整個網路環境都發生了故障

網路分區是指在分散式系統中,不同的節點分布在不同的子網路(機房或異地網路)中,由於一些特殊的原因導致這些子網路出現網路不連通的狀況,但各個子網路的內部網路是正常的,從而導致整個系統的網路環境被切分成了若干個孤立的區域。

需要注意的是,組成一個分散式系統的每個節點的加入與退出都可以看作是一個特殊的網路分區。

既然一個分散式系統無法同時滿足一致性、可用性、分區容錯性三個特點,所以我們就需要拋棄一樣:

用一張表格說明一下:

需要明確的一點是,對於一個分散式系統而言,分區容錯性是一個最基本的要求。因為既然是一個分散式系統,那麼分散式系統中的組件必然需要被部署到不同的節點,否則也就無所謂分散式系統了,因此必然出現子網路。

而對於分散式系統而言,網路問題又是一個必定會出現的異常情況,因此分區容錯性也就成為了一個分散式系統必然需要面對和解決的問題。因此系統架構師往往需要把精力花在如何根據業務特點在C(一致性)和A(可用性)之間尋求平衡。

BASE理論

BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)三個短語的縮寫。BASE理論是對CAP中一致性和可用性權衡的結果,其來源於對大規模互聯網系統分散式實踐的總結,是基於CAP定理逐步演化而來的。

BASE理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,採用適當的方式來使系統達到最終一致性。接下來看一下BASE中的三要素:

1、基本可用

基本可用是指分散式系統在出現不可預知故障的時候,允許損失部分可用性—-注意,這絕不等價於系統不可用。比如:

(1)響應時間上的損失。正常情況下,一個在線搜索引擎需要在0.5秒之內返回給用戶相應的查詢結果,但由於出現故障,查詢結果的響應時間增加了1~2秒

(2)系統功能上的損失:正常情況下,在一個電子商務網站上進行購物的時候,消費者幾乎能夠順利完成每一筆訂單,但是在一些節日大促購物高峰的時候,由於消費者的購物行為激增,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面

2、軟狀態

軟狀態指允許系統中的數據存在中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性,即允許系統在不同節點的數據副本之間進行數據同步的過程存在延時

3、最終一致性

最終一致性強調的是所有的數據副本,在經過一段時間的同步之後,最終都能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終數據能夠達到一致,而不需要實時保證系統數據的強一致性。

總的來說,BASE理論面向的是大型高可用可擴展的分散式系統,和傳統的事物ACID特性是相反的,它完全不同於ACID的強一致性模型,而是通過犧牲強一致性來獲得可用性,並允許數據在一段時間內是不一致的,但最終達到一致狀態

但同時,在實際的分散式場景中,不同業務單元和組件對數據一致性的要求是不同的,因此在具體的分散式系統架構設計過程中,ACID特性和BASE理論往往又會結合在一起。

最後

歡迎大家一起交流,喜歡文章記得點個贊喲,感謝支援!