分佈式初探——講透分佈式系統一致性模型

本文始發於我的公衆號:TechFlow數據庫

在計算機系統的領域,一致性能夠說是一個高頻詞,可能出現的場景不少。從分佈式系統到數據庫的事務,都有它的身影。網絡

以前咱們在介紹數據庫事務的時候,談到過事務的一致性。在數據庫當中,一致性是一種目的,不是一種手段。數據庫但願控制事務的原子性、隔離性和持久性來保證數據的一致性。這裏的一致性更多的指的是實際和咱們觀念的一致。也就是說結果都在咱們預期以內。而在分佈式系統當中,一致性有另外的含義,一個是多份數據副本之間的一致性問題,另外一個是多階段提交的一致性問題。咱們今天先來聊聊副本一致性問題。併發

這個問題出現的緣由很簡單,由於分佈式系統當中,數據每每會有多個副本。若是是一臺數據庫處理全部的數據請求,那麼經過ACID四原則,基本能夠保證數據的一致性。而多個副本就須要保證數據會有多份拷貝。這就帶來了同步的問題,由於咱們幾乎沒有辦法保證能夠同時更新全部機器當中的包括備份全部數據。尤爲是當這些機器分佈在全國各地甚至是世界各地的時候,因爲網絡延遲,即便我在同一時間給全部機器發送了更新數據的請求,也不能保證這些請求被響應的時間保持一致。只要存在時間差,就會存在某些機器之間的數據不一致的狀況。也就是說,在分佈式系統當中的一致性,指的是數據一致性異步

這實際上是一個兩難問題,爲了解決流量過大的壓力問題,咱們設計了分佈式系統。可是分佈式系統又會帶來數據多份拷貝不一樣步違反一致性的問題,咱們既不能容忍數據出錯,也不能放棄分佈式系統,惟一的辦法就是採起一些措施,來最大可能地下降這個問題的影響力。分佈式

多種多樣的一致性模型,就是這些措施的體現。讓咱們從最簡單的嚴格一致性提及。高併發


### 嚴格一致性

性能

嚴格一致性是最理想的狀況,若是咱們每次請求一個數據,無論什麼狀況下,咱們都能得到它的最後一次改動的結果。很遺憾的是,嚴格一致性是不可能實現的。優化

不可能實現的緣由很簡單,由於多臺機器之間的數據同步須要時間,不管這個時間多小,它都是肯定存在的。只要存在,就不可能實現嚴格一致性。舉個簡單的例子,咱們有A和B兩臺機器。在t時刻,A機器修改了某條數據,在1毫秒以前,B機器收到了一條查詢該數據的請求。當B執行這個查詢的時候,A機器已經修改完成,那麼究竟B查詢到的值應該是什麼呢?是A修改以前的仍是修改以後的呢?在A機器看來,B的查詢發生在它修改以後,但是B機器看來卻偏偏相反,A修改值發生了在它收到請求以後。若是咱們要保證嚴格一致性,那麼究竟什麼結果纔是對的呢?設計

固然上面這個例子只是最極端的狀況,通常只會在理論上發生,可是經過對極端狀況的分析,咱們也能夠看得出來,嚴格一致性是不可能實現的。3d


強一致性與弱一致性


數據不一致出現的根本緣由在於多臺機器更新數據的時間差,咱們更新多臺機器,總有先有後,很難保證徹底同步。根據同步數據時採用同步仍是異步策略,又能夠將一致性分爲強一致性與弱一致性

使用同步策略更新數據時,咱們每次請求發給主節點,主節點收到數據以後使用同步更新的策略將數據發送給從節點。當全部的從節點更新成功以後,主節點會更新數據的狀態,使它生效,以後返回response給用戶,告知更新成功。

顯然,經過同步更新數據的策略下,一致性的保障更強。若是咱們在主機上作好隔離措施,好比在更新結束以前,用戶不能發起下一次更新,那麼儘量地保證數據一致性不出問題。可是這種作法也有很大的弊端,最大的弊端就在於使用同步更新的操做,並且要全部從庫都更新成功才能返回,這樣的時間開銷很是大。最關鍵的一個缺點在於,若是一個從庫宕機,那麼主庫就不可能更新全部的從庫,那麼新來的請求永遠不會更新,這顯然是不能接受的。

和強一致性對應的是弱一致性,咱們不採用同步策略來更新數據,而採用異步更新的方式。好處也很明顯,同步改爲了異步,時間消耗大大縮減。可是問題也很明顯,除了異步自己帶來的問題以外,因爲多個副本之間的數據更新發生不一樣時,若是連續屢次訪問落到了不一樣的副本上,就會出現屢次訪問的結果不一致的狀況。

本質上來講分佈式系統的一致性模型,只有強弱兩種。只不過在這兩種基礎的模型上,針對許多可能出現的問題還會進行相應的優化。整體上而言,分佈式系統對於性能的要求要高於一致性,因此大多分佈式系統的一致性模型,仍是基於弱一致性設計的。下面就來列舉幾種,比較經典的弱一致性模型的優化方案。


讀寫一致性


讀寫一致性在平常中常常碰見,好比在某論壇當中,用戶回覆了某個帖子。可是當用戶刷新的時候,可能會出現這個回覆消失的狀況。用戶會陷入困擾,不知道這個回覆究竟有沒有成功

會發生這種狀況的緣由也很簡單,由於用戶刷新的時候,訪問的從庫可能尚未獲取到用戶回覆的數據,因此顯示的結果當中天然就沒有用戶剛剛回復的內容。在這種狀況下,咱們須要保證讀寫一致性。也就是說用戶讀取本身寫入結果的一致性,保證用戶永遠可以第一時間看到本身更新的內容。好比咱們發一條朋友圈,朋友圈的內容是否是第一時間被朋友看見不重要,可是必定要顯示在本身的列表上。

那如何實現呢?

方案有好幾種,一種方案是對於一些特定的內容咱們每次都去主庫讀取。好比咱們讀取帖子當中回覆信息的時候,永遠都去主庫讀取。可是這樣的問題也很明顯,可能會致使主庫的壓力過大。另外一種方案是咱們設置一個更新時間窗口,在剛剛更新的一段時間內,咱們默認都從主庫讀取,過了這個窗口以後,咱們會挑選最近有過更新的從庫進行讀取。

還有一種更好的方案是咱們直接記錄用戶更新的時間戳,在請求的時候把這個時間戳帶上,凡是最後更新時間小於這個時間戳的從庫都不予以響應。也就是說只有包含用戶寫入這個更新的庫能夠響應這個請求,就能夠保證明現用戶端的讀寫性一致了。


單調讀


單調讀解決的是最經典的弱一致性的不一致問題,出現的場景也很簡單。因爲主從節點更新數據的時間不一致,致使用戶在不停地刷新的時候,有時候能刷出來,再次刷新以後會發現數據不見了,再刷新又可能再刷出來,就好像碰見靈異事件同樣。我記得之前微博或者人人就存在這個問題,單調讀就是針對的這個場景,能夠保證不會出現這種狀況。

解決的方法其實很簡單,就是根據用戶ID計算一個hash值,再經過hash值映射到機器。同一個用戶無論怎麼刷新,都只會被映射到同一臺機器上。這樣就保證了不會讀到其餘從庫的內容,帶來用戶體驗很差的影響。固然,這只是一種解決方案,其餘的解決方法還有不少,這裏不一一討論。


因果一致性


因果一致性針對的數據之間邏輯上的因果問題,舉個例子,好比說用戶在知乎裏提問題和回答問題。想要回答問題,必需要保證有對應的問題。也就是說必定是先有問題,再有的回答。但是問題和回答並不必定存儲在同一個節點上,頗有可能出現問題存入節點A,回答存入節點B的狀況。由於存在同步延遲,因此就可能出現查詢的用戶只看獲得回答,卻找不到對應問題的狀況,違反了事物之間的因果性

爲了解決這個問題,一種方案是在寫入的時候遵循某種邏輯順序,那麼在讀取的時候,就能夠保證不會出現因果錯亂的狀況。但問題是,不少因果性並不想問題和回答這麼明顯,一些隱藏的因果性可能很難被輕易判斷,就須要引入更高深的技術,感興趣的同窗能夠去搜索一下向量時鐘深刻了解。


最終一致性


聽名字這種方案彷佛很厲害,其實最終一致性是全部分佈式一致性模型當中最弱的。能夠認爲是沒有任何優化的「最」弱一致性,它的意思是說,我不考慮全部的中間狀態的影響,只保證當沒有新的更新以後,通過一段時間以後,最終系統內全部副本的數據是正確的

有大神打了這麼一個比方,就好像你去點星巴克。你並不知道星巴克何時作好,你在作好以前去拿,拿到的結果是錯的。可是你知道,通過一段時間以後,你必定能夠拿到你想要的。至於星巴克作好須要多久,每每沒有定論,能夠是幾百分之一秒,也能夠是幾個小時。

聽起來這個方案很不靠譜,在肯定它可行以前,咱們還想問幾個問題,首先,系統能不能保證一段時間是多久?若是天荒地老怎麼辦?其次,由於最終才能收斂,那麼在收斂以前,多個副本之間的值可能都不一樣,究竟又該以哪一個爲準?

好在,這兩個問題都能回答。對於第一個問題,答案是系統沒辦法肯定究竟須要多久收斂,可是能夠肯定最大的收斂時間。有點像是物理學上的半衰期,咱們不知道一個粒子究竟須要多久衰變,可是能夠肯定足夠多的粒子當中的一半衰變所須要的時間。第二個問題更好回答,當有多份數據出現的時候,一般的作法是選擇其中時間戳較大的,也就是說出現較晚的值

雖然最終一致性看起來很不靠譜,可是它最大程度上保證了系統的併發能力,也所以,在高併發的場景下,它也是使用最廣的一致性模型。

到這裏爲止,分佈式系統當中常見的一致性模型就介紹完了。分佈式系統有一個很大的特色,就是咱們看專業名詞的時候每每雲裏霧裏,不知所云。可是當咱們去了解它背後的設計理念與出現的緣由,就能發現它的有趣。衷心但願你們都能從中發現本身的樂趣,都有學有收穫。

若是喜歡本文,請順手點個關注吧,大家的支持是我最大的動力。

相關文章
相關標籤/搜索