在後臺開發中,當咱們須要支持大規模的併發讀寫,同時具有橫向擴展能力。這這時咱們通常會對數據進行hash分區,從而分佈到不一樣的服務器中,以解決寫的瓶頸。同時每一個寫服務器會經過主從同步分佈到多臺服務器上面,從而實現讀寫分離,提升讀的併發性能。
那麼問題來了,一次性寫多條數據,怎麼保證多臺服務器的一致性呢?若是數據同步存在延遲,怎麼保證寫後必定能讀到呢?本文咱們就探討一下一致性的問題。數據庫
一、咱們一般把一致性分爲最終一致性與強一致性,那麼什麼是最終一致性,什麼是強一致性呢?
最終一致性(Eventually):寫入數據 A 成功後,在其餘副本有可能讀不到 A 的最新值,但在某個時間窗口以後保證最終能讀到。注意這裏有限定某個時間窗口。
強一致性(Strong):數據 A 一旦寫入成功,在任意副本任意時刻都能讀到 A 的最新值。
從定義中咱們能夠看出,這種分類方法太過於籠統,不利於實際應用。故咱們通常從數據的維度或者用戶的維度進行分類。
二、數據維度的一致性:就是A服務器的數據與B服務器的數據是否一致。數據維度的一致性經常使用的有以下幾種:
嚴格一致性,也叫線性一致性、原子一致性。在分佈式系統中,若是某個進程更新了數據,那麼在其餘進程必須能讀取到這個最新的數據。在分佈式系統中若是要實現嚴格一致性,通常讀寫都須要在同一臺服務器上,若是讀寫分佈在不一樣的服務器上,即便使用二階段提交,因爲網絡有時延,也不可能作到嚴格的一致性。舉一個購物庫存的例子,若是A客戶看到評估11庫存有10臺,若是其餘客戶也都看到庫存是10臺,那麼就知足強一致性,可是若是某個客戶B看到了庫存是11臺,那麼不知足強一致性。一個典型的應用場景就是,若是業務須要知足強一致性要求,那麼會到MySQL的主庫去讀寫。
順序一致性是指,全部的進程以相同的順序看到全部的修改。讀操做未必能及時獲得此前其餘進程對同一數據的寫更新,可是每一個進程讀到的該數據的不一樣值的順序是一致的。舉個例子,zk寫的時候,ZAB協議只保證超過半數的進程寫入成功就返回了,可是客戶端去讀的時候,多是從任意的進程讀取的,故某個客戶端就有可能讀到舊的數據庫,可是ZAB能夠保證按照主進程的寫入順序寫入其餘進程,所以客戶端去讀的時候是知足順序一致性的。在好比聊天時,若是在手機端與電腦端看到的聊天內容的順序是一致的,那麼就知足順序一致性。
因果一致性是一種弱化的順序一致性,全部進程必須以相同的順序看到具備潛在因果關係的寫操做,不一樣進程能夠以不一樣的順序看到併發的寫操做。MySQL5.7開始的數據庫的主從同步,其實就是知足因果一致性。在好比一個客戶先購買了某隻股票,後面又賣了。那麼無論在哪裏看到的都是先買了某隻股票,再賣了某隻股票,那麼就是知足因果一致性的。咱們大倉風控的主從同步,就是知足因果一致性的。
三、用戶維度的一致性,主要包括:
單調讀一致性是指,若是一個進程讀取數據項 a 的值,那麼該進程對 a 執行的任何後續讀操做,老是獲得第一次讀取的那個值或更新的值。說白了,就是不能讀到新數據後,再讀到比這個數據還舊的數據。好比在微博的場景中,某個明星發佈了一條微博,若是某個粉絲讀到了這條微博,再讀到這條微博事後,從新打開APP,發現那條微博不見了(明星沒有刪除微博),那麼就不知足單調一致性。
單調寫一致性是指,一個進程對數據項 a 執行的寫操做,必須在該進程對 a 執行任何後續寫操做前完成。這個很容易知足,注意這裏是一個進程,全部的寫操做都是順序的。
寫後讀一致性是指,一個進程對數據項 a 執行一次寫操做的結果,老是會被該進程對 a 執行的後續讀操做看見。這個比較常見,好比數據庫採用 Master-Slave 結構部署時,寫完 Master 數據庫,若是從 Slave 讀取,有可能讀不到,就不知足寫後讀一致性了。再具體一點的例子就是,某個明星發佈了一條微博,可是這個明星再次打開APP時,若是看不到本身發佈的微博,那麼就不知足寫後讀一致性。
讀後寫一致性是指,同一進程對數據項 a 執行的讀操做以後的寫操做,保證發生在與讀時的版本上或者更新的版本上。好比下文介紹的Quorum機制,寫的服務器的數據版本是有可能比讀的時候舊的,若是寫成功了,那麼就不知足讀後寫一致性。服務器
上文提到高併發寫的時候,咱們經過多機器hash處理,從而實現每一個機器的寫併發都不會太大,可是業務通常都有都多寫少的特色,故咱們又經過複製多個副本,而後讀操做都在副本上面實現,那麼咱們要如何作到讀後寫一致性呢?
好比 MySQL,就能夠採用一個 Master,多個 Slave 的方式,全部的寫都在 Master 上更新,全部的讀都在 Slave 上進行,但這裏存在一個問題,就是怎麼保證寫後讀一致性?咱們能夠監控讀寫的延遲,而後設置一個最大的讀寫延遲的閾值,若是寫後讀再閾值之內,那麼就去master讀,若是超過了閾值,就去slave讀。
前面的方法是隻再主節點裏面寫數據,那麼咱們是否能夠一次寫多個節點呢?這時Quorum 機制(NWR 模型)就派上用場了。
Quorum 機制就是要知足公式 W+R > N。其中,W 表示必須至少寫入成功的節點數,R 表示至少讀取成功的節點數,N 表示總節點數。這個公式把選擇權交給了業務用戶,讓用戶來作出最終決策。那麼業務端怎麼知道讀取出來的就是最新的數據呢?就是利用版本號,每次寫數據的同時,都記錄這條數據對應的版本號,讀數據的時候判斷版本號,版本最新的就是最終數據。這裏還存在另一個問題,那就是,若是寫的時候只寫入的節點<=N/2,當存在併發寫操做時,版本會存在衝突,這就要求咱們寫數據的時候最好是遵循 W>N/2。網絡
一、前面介紹的只再主節點寫數據,可是若是主節點掛了,就要選一個從節點做爲新的主節點。那麼咱們如何才能保證選出來的主節點的數據不丟失呢?這時就涉及到分佈式共識問題了,我後面會在分佈式共識裏面介紹。
二、前面介紹的經過hash到不一樣的服務器,那麼經常使用的hash方法都有哪些呢?怎麼作到一個工業級的hash呢?併發