當前位置:
首頁 > 知識 > 深入淺析mongodb的一致性模型及其實現

深入淺析mongodb的一致性模型及其實現

我們已經知道,在zookeeper中,寫請求轉發給leader,讀請求follower自己處理,沒有sync的情況下達不到linearizability。Mongodb呢?官方文檔是這麼說的,「MongoDB is consistent by default: reads and writes are issued to the primary member of a replica set. 」

這句話誤導了我很多年,讓我一直以為默認情況下,讀寫操作都發給primary,這總應該linearizable了吧?結果得知真相之後讓我眼淚掉下來。

Write Concern,Read Concern和Read Preference

Mongodb能夠提供靈活的一致性,原因在於它可以設置不同的Write Concern,Read Concern和Read Preference。

Write Concern決定了對資料庫寫入操作的確認級別,比如是否大多數節點都寫入成功了才向客戶端返回確認。

Read Concern決定了讀取操作的返回數據的隔離級別,比如是否將大多數節點都希爾成功的數據返回給讀操作。

Read Preference決定了從哪個節點進行讀取操作。

默認情況

在mongodb 4.0版本中,默認的Read Preference是primary,也就是讀寫操作都發送給primary。

默認的Write Concern是w:1,對於副本集來說,就是只要primary確認寫入就可以了。所以你以為你寫入成功了,但是primary沒有把數據分發出去之前就掛掉了,那麼你以為寫入成功的數據,就消失得無影無蹤了。默認的Read Concern呢?居然是local!所以你從primary讀到了一個數據x,在x還沒被分發時primary掛了,新的primary沒有x的信息,那麼再從新的primary讀數據時,x就好像從來沒存在過一樣。

所以,默認情況下,mongodb的一致性和Linearizability差得遠呢。

Majority

那把Write Concern和Read Concern都設置為majority會怎麼樣呢?

Majority的Write Concern指的是數據分發給副本集的大多數成員之後再確認。

Majority的Read Concern指的是讀取到的數據是被副本集大多數成員確認的。

想像以下情況:

N1是副本集的primary,N2和N3是兩個secondary。客戶端S1讀寫請求都發給N1。

N1突然發生了網路隔離,不能和N2,N3通信了。

N2被選舉為primary,但是N1對此一無所知,仍然認為自己是primary,所以此時副本集里實際上有兩個primary。

客戶端S2以majority的Write Concern寫入新的數據到N2。

N2將數據寫入N3後,向S2返回寫入確認。

客戶端S1以majority的Read Concern從N1讀取數據。

很容易看出,這種情況下是沒有linearizability的。

其實,Mongodb還有一種更糟的情況,N1和N2同時作為primary的時候,S1有可能寫請求發給N2,讀請求發給N1。這樣它連自己寫入的數據都讀不出來。因為mongodb的driver一般都需要連接池,而mongodb又有Read Preference這種實現讀寫分離的選項,所以實現起來就很麻煩。driver除了官方的以外,還有開源社區維護的,沒注意到這些細節的話,很容易出現這種問題。

而且早期的mongodb中,根本沒給你檢測stale primary的機會。。。

https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#using-setversion-and-electionid-to-detect-stale-primaries

Linearizable Read Concern

Linearizable Read Concern是mongodb 3.4中出現的,為了解決上面的問題。有了linearizable的Read Concern,終於可以讀取副本集中最新的數據啦。Linearizable Read Concern有一些使用上的限制:

只能用在primary的read操作上

查詢條件必須唯一的確定一個document

linearizable Read Concern的實現原理是在read操作之前添加了一個空的寫操作,如果當前的primary是stale的,空的寫操作就會失敗,從而被檢查出來。實際上和zookeeper的sync如出一轍殊途同歸。

Causal Consistency

上面的例子中,client的讀寫都發給了primary,不過總讓secondary閑著也不太好,不如搞個讀寫分離吧。

不過一開始的讀寫分離是容易出現問題的:

N1是副本集的primary,N2和N3是兩個secondary。

客戶端S設置Read Preference,可以讓讀請求發給secondary,寫請求發給primary。

S在N1上以majority的Write Concern寫入訂單數據x。

N1把x分發給了N2之後,判定超過半數了,返回成功。

S在N3上讀取x,發現沒有這個訂單。

這真是非常尷尬,自己寫的數據,自己都看不到。所以mongodb 3.6增加了Causal Consistency一致性。

為了實現它,mongodb每個節點都需要能獲取正確的時間。時鐘是分散式系統非常重要的組成部分,在其他文章中我們會著重介紹。

現在mongodb這樣工作:

N1是副本集的primary,N2和N3是兩個secondary。

客戶端S設置Read Preference,可以讓讀請求發給secondary,寫請求發給primary。

S在N1上以majority的Write Concern寫入訂單數據x,記錄時間T。

N1把x分發給了N2之後,判定超過半數了,返回成功。

S在N3上讀取x,讀取操作帶有T作為參數。

N3沒有時間T的操作,開始等待。

N3從N1得到x的數據,意味著N3擁有了T時間的數據。

N3將數據返回給S。

為了達到Causal Consistency,mongodb的客戶端需要額外進行一些操作,例如開啟client session,同時也有相應的一些限制,例如同一時刻只能有一個線程在client session中執行操作。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 千鋒JAVA開發學院 的精彩文章:

大規模系統的消息隊列技術方案!

TAG:千鋒JAVA開發學院 |