在分布式應(yīng)用系統(tǒng)中,mongodb 已經(jīng)成為 NoSQL 經(jīng)典數(shù)據(jù)庫。要想很好的使用 mongodb,僅僅知道如何使用它是不夠的。只有對其架構(gòu)原理等有了充分認識,才能在實際運用中使其更好地服務(wù)于應(yīng)用,遇到問題知道怎么處理,而不是抓瞎抹黑。這篇文章就帶你進入 mongodb 集群的大門。
集群概覽
mongodb 相關(guān)的進程分為三類:
- mongo 進程 – 該進程是 mongodb 提供的 shell 客戶端進程,通過該客戶端可以發(fā)送命令并操作集群;
- mongos 進程 – mongodb 的路由進程,負責與客戶端連接,轉(zhuǎn)發(fā)客戶端請求到后端集群,對客戶端屏蔽集群內(nèi)部結(jié)構(gòu);
- mongod 進程 – 提供數(shù)據(jù)讀寫的 mongodb 實例進程。
類比銀行服務(wù),mongo 進程相當于客戶,mongos 進程是柜臺服務(wù)員,mongod 進程是銀行后臺實際處理業(yè)務(wù)的人員或者流程。客戶只需要和柜臺服務(wù)員溝通,告知辦什么業(yè)務(wù),柜臺服務(wù)員將業(yè)務(wù)轉(zhuǎn)往后臺,后臺實際處理。
下圖是 mongodb 集群的一般拓撲結(jié)構(gòu)。
如圖,mongodb 集群的節(jié)點分為三類:
- mongos 路由節(jié)點:處理客戶端的連接,扮演存取路由器的角色,將請求分發(fā)到正確的數(shù)據(jù)節(jié)點上,對客戶端屏蔽分布式的概念;
- config 配置節(jié)點:配置服務(wù),保存數(shù)據(jù)結(jié)構(gòu)的元數(shù)據(jù),比如每個分片上的數(shù)據(jù)范圍,數(shù)據(jù)塊列表等。配置節(jié)點也是 mongod 進程,只是它存儲的數(shù)據(jù)是集群相關(guān)的元數(shù)據(jù);
- shard 分片節(jié)點:數(shù)據(jù)存儲節(jié)點,分片節(jié)點由若干個副本集組成,每個副本集存儲部分全體數(shù)據(jù),所有副本集的數(shù)據(jù)組成全體數(shù)據(jù),而副本集內(nèi)部節(jié)點存放相同的數(shù)據(jù),做數(shù)據(jù)備份與高可用。
還是拿銀行業(yè)務(wù)類比,當客戶辦理保單保存業(yè)務(wù)時,
- 柜臺服務(wù)員接受客戶的保單業(yè)務(wù)請求(mongos 路由節(jié)點接收客戶端的操作請求);
- 柜臺服務(wù)員查詢文件目錄系統(tǒng)查看該保單應(yīng)該保存到哪個倉庫(mongos 節(jié)點與 config 配置節(jié)點通信,查詢相關(guān)操作數(shù)據(jù)在哪個分片節(jié)點);
- 知道哪個倉庫后,柜臺服務(wù)員將保單給倉庫管理員,倉庫管理員將保單放到指定倉庫中(mongos 節(jié)點將請求發(fā)送給數(shù)據(jù)所在分片節(jié)點,分片節(jié)點進行讀寫處理)。
mongos 路由服務(wù)
mongos 服務(wù)類似網(wǎng)關(guān),連接 mongodb 集群與應(yīng)用程序,對外屏蔽 mongodb 內(nèi)部結(jié)構(gòu),應(yīng)用程序只需要將請求發(fā)送給 mongos,而無需關(guān)心集群內(nèi)部副本分片等信息。
mongos 本身不保存數(shù)據(jù)與索引信息,它通過查詢 config 配置服務(wù)來獲取,所以可以考慮將 mongos 與應(yīng)用程序部署在同一臺服務(wù)器上,當服務(wù)器宕機時 mongos 也一起失效,防止出現(xiàn) mongos 閑置。
mongos 節(jié)點也可以是單個節(jié)點,但為了高可用,一般部署多個節(jié)點。就像柜臺服務(wù)員一樣,可以有多個,相互之間沒有主備關(guān)系,都可以獨立處理業(yè)務(wù)。
需要注意的是,在開啟分片的情況下,應(yīng)用程序應(yīng)該避免直接連接分片節(jié)點進行數(shù)據(jù)修改,因為這種情況下很可能造成數(shù)據(jù)不一致等嚴重后果,而是通過 mongos 節(jié)點來操作。
config 配置服務(wù)
config 配置節(jié)點本質(zhì)也是一個副本集,副本集中存放集群的元數(shù)據(jù),如各個分片上的數(shù)據(jù)塊列表,數(shù)據(jù)范圍,身份驗證等信息。如下,可以看到數(shù)據(jù)庫 config,數(shù)據(jù)庫中集合保存了集群的重要元數(shù)據(jù)。
mongos> use config;
switched to db config
mongos> show collections;
changelog
chunks
collections
databases
lockpings
locks
migrations
mongos
shards
tags
transactions
version
一般情況下,用戶不應(yīng)該直接變更 config 的數(shù)據(jù),否則很可能造成嚴重后果。
shard 分片服務(wù)
分布式存儲要解決的是兩個問題:
隨著業(yè)務(wù)不斷發(fā)展,數(shù)據(jù)量越來越大,單機存儲受限于物理條件,必然要通過增加服務(wù)器來支持不斷增大的數(shù)據(jù)。所以分布式下,不可能全部數(shù)據(jù)存儲在一個節(jié)點上,必然是將數(shù)據(jù)劃分,部分數(shù)據(jù)放到這個節(jié)點,另外部分數(shù)據(jù)放到另外的節(jié)點上。也就是數(shù)據(jù)的伸縮性。
考慮高可用。如果同一份數(shù)據(jù)只存在一個節(jié)點上,當這個節(jié)點發(fā)生異常時,數(shù)據(jù)不可用。這就要求分布式下同一份數(shù)據(jù)需要存儲在多個節(jié)點上,以達高可用效果。
在 mongodb 集群中,數(shù)據(jù)的伸縮性通過分片集來實現(xiàn),高可用通過副本集來實現(xiàn)。
如圖,全部數(shù)據(jù)為1-6,將其劃分為3部分,1-2為一個分片,3-4為一個分片,5-6為一個分片。每個分片存儲在不同的節(jié)點上。而每個分片有3個副本,組成副本集,每個副本都是獨立的 mongod 實例。
所以副本集是一個縱向概念,描述的是相同的數(shù)據(jù)存儲在多個節(jié)點上;而分片是一個橫向概念,描述的是全量數(shù)據(jù)被切成不同的片段,每個片段獨立存儲。這個片段就是分片,而分片通過副本集進行存儲。
副本集
副本集包含三種角色:
- 主節(jié)點(Primary)
- 副節(jié)點(Secondary)
- 仲裁節(jié)點(Arbiter)
一個副本集由一個主節(jié)點,多個副節(jié)點,0或多個仲裁節(jié)點組成。
主節(jié)點與副節(jié)點是數(shù)據(jù)節(jié)點。主節(jié)點提供數(shù)據(jù)的寫操作,數(shù)據(jù)寫到主節(jié)點后,會通過同步機制同步到副節(jié)點上。默認讀操作也由主節(jié)點提供,但是可以手動設(shè)置 read preference,優(yōu)先從副節(jié)點讀取。
仲裁節(jié)點不是數(shù)據(jù)節(jié)點,不存儲數(shù)據(jù),也不提供讀寫操作。仲裁節(jié)點是作為投票者存在,當主節(jié)點異常需要進行切換時,仲裁節(jié)點有投票權(quán),但沒有被投票權(quán)。仲裁節(jié)點可以在資源有限的情況下,依然支持故障恢復(fù)。比如只有2個節(jié)點的硬盤資源,在這種情況下可以增加一個不占存儲的仲裁節(jié)點,組成“一主一副一仲裁”的副本集架構(gòu),當主節(jié)點宕掉時,副節(jié)點能夠自動切換。
節(jié)點間通過“心跳”進行溝通,以此知道彼此的狀態(tài)。當主節(jié)點異常不可用時,從其他有被投票權(quán)的節(jié)點中投票選出一個升級為主節(jié)點,繼續(xù)保持服務(wù)高可用。這里投票采取“大多數(shù)”原則,即需要多于總節(jié)點數(shù)一半的節(jié)點同意,才能被選舉成主節(jié)點。也因此不建議采用偶數(shù)個節(jié)點組成副本集,因為偶數(shù)情況下,如果發(fā)生半數(shù)節(jié)點網(wǎng)絡(luò)隔離,隔離的半數(shù)節(jié)點達不到“大多數(shù)”的要求,無法選舉產(chǎn)生新的主節(jié)點。
通過 rs.status() 可以查看副本集,參考《教你快速搭建 mongodb 集群》
分片集
分片就是將全部數(shù)據(jù)根據(jù)一定規(guī)則劃分成沒有交集的數(shù)據(jù)子集,每個子集就是一個分片,不同分片存放在不同節(jié)點上。這里有幾個問題:
- 劃分規(guī)則也就是分片策略是什么?
- 分片數(shù)據(jù)是如何存放的?
- 數(shù)據(jù)量越來越大,分片如何動態(tài)調(diào)整?
數(shù)據(jù)塊 Chunk
chunk 由多個文檔組成,一個分片中包含多個 chunk。chunk 是分片間數(shù)據(jù)遷移的最小單位。實際上,文檔是通過分片策略計算出應(yīng)該存儲在哪個 chunk,而 chunk 存放在分片上。
如圖,假設(shè)按照文檔的 x 字段值來進行分片,根據(jù)不同取值范圍存放在不同的數(shù)據(jù)塊,如25-175在 chunk 3上。
把書比作 mongodb 中的文檔,書柜比作數(shù)據(jù)塊,房間比作分片。每本書根據(jù)一定規(guī)則放到某書柜上,房間中有很多書柜。當某個房間的書柜太多,就需要以書柜為單位,遷移到相對比較寬松的房間。
chunk 的大小默認為 64MB,也可以自定義。chunk 的存在有兩個意義:
- 當某個 chunk 超過大小時,會觸發(fā) chunk 分裂。
- 當分片間的 chunk 數(shù)不均衡時,會觸發(fā) chunk 遷移。
chunk 遷移由 mongodb 的平衡器來操作,默認平衡器是開啟的,是運行在后臺的一個進程,也可以手動關(guān)閉。
可以通過下面命令來查看平衡器狀態(tài):
chunk 的大小對集群的影響:
- 比較小時,chunk 數(shù)比較多,數(shù)據(jù)分布比較均勻,但會引起頻繁的數(shù)據(jù)塊分裂與遷移;
- 比較大時,chunk 數(shù)比較少,數(shù)據(jù)容易分散不均勻,遷移時網(wǎng)絡(luò)傳輸量大。
所以要自定義數(shù)據(jù)塊大小時,一定要考慮完備,否則將大大影響集群與應(yīng)用程序的性能。
片鍵 Shard Key
mongodb 集群不會自動將數(shù)據(jù)進行分片,需要客戶端告知 mongodb 哪些數(shù)據(jù)需要進行分片,分片的規(guī)則是什么。
某個數(shù)據(jù)庫啟用分片:
mongos> sh.enableSharding(database>)
設(shè)置集合的分片規(guī)則:
mongos> sh.shardCollection(database.collection>,key>,unique>,options>)
# unique 與 options 為可選參數(shù)
例如,將數(shù)據(jù)庫 mustone 開啟分片,并設(shè)置庫中 myuser 集合的文檔根據(jù) _id 字段的散列值來進行劃分分片。
sh.enableSharding("mustone")
sh.shardCollection("mustone.myuser",{_id: "hashed"})
這里劃分規(guī)則體現(xiàn)在 上, 定義了分片策略,分片策略由片鍵 Shard Key 與分片算法組成。片鍵就是文檔的某一個字段,也可以是復(fù)合字段。分片算法分為兩種:
- 基于范圍。如 設(shè)置為 id:1 表示基于字段 id 的升序進行分片,id:-1 表示基于字段 id 的倒序進行分片,字段 id 就是 shard key(片鍵)。當集合中文檔為空時,設(shè)置分片后,會初始化單個 chunk,chunk 的范圍為(-∞,+∞)。當不斷往其中插入數(shù)據(jù)到達 chunk 大小上限后,會進行 chunk 分裂與必要遷移。
- 基于hash。如上面的栗子, 設(shè)置為 _id:”hashed”,表示根據(jù)字段 _id 的哈希來分片,此時片鍵為 _id。初始化時會根據(jù)分片節(jié)點數(shù)初始化若干個 chunk,如3個分片節(jié)點會初始化6個 chunk,每個 shard 2個 chunk。
每個數(shù)據(jù)庫會分配一個 primary shard,初始化的 chunk 或者沒有開啟分片的集合都默認放在這個 primary shard 上。
分片策略的選擇至關(guān)重要,等數(shù)據(jù)量大了再更改分片策略將會很麻煩。分片策略的原則:
- 均勻分布原則。分片的目標就是讓數(shù)據(jù)在各個分片上均勻分布,數(shù)據(jù)的存取壓力也分解到各個分片上。比如以自增長的 id 升序為片鍵,會導致新數(shù)據(jù)永遠都寫在最后的 chunk 上,且 chunk 分裂與遷移也會落在該 chunk 所在分片上,造成該分片壓力過大。
- 大基數(shù)原則。集合的片鍵可能包含的不同值的個數(shù),稱為基數(shù)?;鶖?shù)越大,數(shù)據(jù)就能劃分得更細。基數(shù)越小,chunk 的個數(shù)就有限。比如性別,只有男女,如果作為片鍵,最多兩個 chunk,等數(shù)據(jù)越來越大后,便無法橫向擴展。
- 就近原則。盡可能讓一次查詢的數(shù)據(jù)分布在同一個 chunk 上,這樣提升磁盤讀取性能。避免毫無意義的隨機片鍵,雖然分布均勻了,但每次查詢都要跨多個 chunk 才能完成,效率低下。
需要說明的是,mongodb 分片集群雖然比較完備,但是存在一些限制,如備份相對困難,分片集合無法做關(guān)聯(lián)查詢等。所以要根據(jù)實際業(yè)務(wù)來評估,如果副本集已經(jīng)夠用了,不一定要進行分片存取。
以上就是深入了解MongoDB 分布式集群的詳細內(nèi)容,更多關(guān)于MongoDB 分布式集群的資料請關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- MongoDB實現(xiàn)基于關(guān)鍵詞的文章檢索功能(C#版)
- 開源 5 款超好用的數(shù)據(jù)庫 GUI 帶你玩轉(zhuǎn) MongoDB、Redis、SQL 數(shù)據(jù)庫(推薦)
- JAVA代碼實現(xiàn)MongoDB動態(tài)條件之分頁查詢
- MongoDB設(shè)計方法以及技巧示例詳解
- MongoDB數(shù)據(jù)庫基礎(chǔ)操作總結(jié)
- express+mongoose實現(xiàn)對mongodb增刪改查操作詳解
- win7平臺快速安裝、啟動mongodb的方法
- 使用Mongodb實現(xiàn)打卡簽到系統(tǒng)的實例代碼
- 淺析MongoDB 全文檢索