MongoDB之 Sharded cluster架構(gòu)原理
為什么需要Sharded cluster?
MongoDB目前3大核心優(yōu)勢(shì):『靈活模式』+ 『高可用性』 + 『可擴(kuò)展性』,通過(guò)json文檔來(lái)實(shí)現(xiàn)靈活模式,通過(guò)復(fù)制集(https://yq.aliyun.com/articles/64?spm=0.0.0.0.9jrPm8)來(lái)保證高可用,通過(guò)Sharded cluster來(lái)保證可擴(kuò)展性。
當(dāng)MongoDB復(fù)制集遇到下面的業(yè)務(wù)場(chǎng)景時(shí),你就需要考慮使用Sharded cluster
●存儲(chǔ)容量需求超出單機(jī)磁盤容量
●活躍的數(shù)據(jù)集超出單機(jī)內(nèi)存容量,導(dǎo)致很多請(qǐng)求都要從磁盤讀取數(shù)據(jù),影響性能
●寫IOPS超出單個(gè)MongoDB節(jié)點(diǎn)的寫服務(wù)能力
如上圖所示,Sharding Cluster使得集合的數(shù)據(jù)可以分散到多個(gè)Shard(復(fù)制集或者單個(gè)Mongod節(jié)點(diǎn))存儲(chǔ),使得MongoDB具備了橫向擴(kuò)展(Scale out)的能力,豐富了MongoDB的應(yīng)用場(chǎng)景。
Sharded cluster架構(gòu)
Sharded cluster由Shard、Mongos和Config server 3個(gè)組件構(gòu)成。
Mongos是Sharded cluster的訪問(wèn)入口,強(qiáng)烈建議所有的管理操作、讀寫操作都通過(guò)mongos來(lái)完成,以保證cluster多個(gè)組件處于一致的狀態(tài)。
Mongos本身并不持久化數(shù)據(jù),Sharded cluster所有的元數(shù)據(jù)都會(huì)存儲(chǔ)到Config Server(下一節(jié)詳細(xì)介紹),而用戶的數(shù)據(jù)則會(huì)分散存儲(chǔ)到各個(gè)shard。Mongos啟動(dòng)后,會(huì)從config server加載元數(shù)據(jù),開始提供服務(wù),將用戶的請(qǐng)求正確路由到對(duì)應(yīng)的Shard。
數(shù)據(jù)分布策略
Sharded cluster支持將單個(gè)集合的數(shù)據(jù)分散存儲(chǔ)在多個(gè)shard上,用戶可以指定根據(jù)集合內(nèi)文檔的某個(gè)字段即shard key來(lái)分布數(shù)據(jù),目前主要支持2種數(shù)據(jù)分布的策略,范圍分片(Range based sharding)或hash分片(Hash based sharding)。
范圍分片
如上圖所示,集合根據(jù)x字段來(lái)分片,x的取值范圍為[minKey, maxKey](x為整型,這里的minKey、maxKey為整型的最小值和最大值),將整個(gè)取值范圍劃分為多個(gè)chunk,每個(gè)chunk(通常配置為64MB)包含其中一小段的數(shù)據(jù)。
Chunk1包含x的取值在[minKey, -75)的所有文檔,而Chunk2包含x取值在[-75, 25)之間的所有文檔… 每個(gè)chunk的數(shù)據(jù)都存儲(chǔ)在同一個(gè)Shard上,每個(gè)Shard可以存儲(chǔ)很多個(gè)chunk,chunk存儲(chǔ)在哪個(gè)shard的信息會(huì)存儲(chǔ)在Config server種,mongos也會(huì)根據(jù)各個(gè)shard上的chunk的數(shù)量來(lái)自動(dòng)做負(fù)載均衡。
范圍分片能很好的滿足『范圍查詢』的需求,比如想查詢x的值在[-30, 10]之間的所有文檔,這時(shí)mongos直接能將請(qǐng)求路由到Chunk2,就能查詢出所有符合條件的文檔。
范圍分片的缺點(diǎn)在于,如果shardkey有明顯遞增(或者遞減)趨勢(shì),則新插入的文檔多會(huì)分布到同一個(gè)chunk,無(wú)法擴(kuò)展寫的能力,比如使用_id作為shard key,而MongoDB自動(dòng)生成的id高位是時(shí)間戳,是持續(xù)遞增的。
Hash分片
Hash分片是根據(jù)用戶的shard key計(jì)算hash值(64bit整型),根據(jù)hash值按照『范圍分片』的策略將文檔分布到不同的chunk。
Hash分片與范圍分片互補(bǔ),能將文檔隨機(jī)的分散到各個(gè)chunk,充分的擴(kuò)展寫能力,彌補(bǔ)了范圍分片的不足,但不能高效的服務(wù)范圍查詢,所有的范圍查詢要分發(fā)到后端所有的Shard才能找出滿足條件的文檔。
合理的選擇shard key
選擇shard key時(shí),要根據(jù)業(yè)務(wù)的需求及『范圍分片』和『Hash分片』2種方式的優(yōu)缺點(diǎn)合理選擇,同時(shí)還要注意shard key的取值一定要足夠多,否則會(huì)出現(xiàn)單個(gè)jumbo chunk,即單個(gè)chunk非常大并且無(wú)法分裂(split);比如某集合存儲(chǔ)用戶的信息,按照age字段分片,而age的取值非常有限,必定會(huì)導(dǎo)致單個(gè)chunk非常大。
Mongos
Mongos作為Sharded cluster的訪問(wèn)入口,所有的請(qǐng)求都由mongos來(lái)路由、分發(fā)、合并,這些動(dòng)作對(duì)客戶端driver透明,用戶連接mongos就像連接mongod一樣使用。
Mongos會(huì)根據(jù)請(qǐng)求類型及shard key將請(qǐng)求路由到對(duì)應(yīng)的Shard
查詢請(qǐng)求
●查詢請(qǐng)求不包含shard key,則必須將查詢分發(fā)到所有的shard,然后合并查詢結(jié)果返回給客戶端
●查詢請(qǐng)求包含shard key,則直接根據(jù)shard key計(jì)算出需要查詢的chunk,向?qū)?yīng)的shard發(fā)送查詢請(qǐng)求
寫請(qǐng)求
寫操作必須包含shard key,mongos根據(jù)shard key算出文檔應(yīng)該存儲(chǔ)到哪個(gè)chunk,然后將寫請(qǐng)求發(fā)送到chunk所在的shard。
更新/刪除請(qǐng)求
更新、刪除請(qǐng)求的查詢條件必須包含shard key或者_(dá)id,如果是包含shard key,則直接路由到指定的chunk,如果只包含_id,則需將請(qǐng)求發(fā)送至所有的shard。
其他命令請(qǐng)求
除增刪改查外的其他命令請(qǐng)求處理方式都不盡相同,有各自的處理邏輯,比如listDatabases命令,會(huì)向每個(gè)Shard及Config Server轉(zhuǎn)發(fā)listDatabases請(qǐng)求,然后將結(jié)果進(jìn)行合并。
Config Server
config database
Config server存儲(chǔ)Sharded cluster的所有元數(shù)據(jù),所有的元數(shù)據(jù)都存儲(chǔ)在config數(shù)據(jù)庫(kù),3.2版本后,Config Server可部署為一個(gè)獨(dú)立的復(fù)制集,極大的方便了Sharded cluster的運(yùn)維管理。
mongos> use config
switched to db config
mongos> db.getCollectionNames()
[
"shards",
"actionlog",
"chunks",
"mongos",
"collections",
"lockpings",
"settings",
"version",
"locks",
"databases",
"tags",
"changelog"
]
config.shards
config.shards集合存儲(chǔ)各個(gè)Shard的信息,可通過(guò)addShard、removeShard命令來(lái)動(dòng)態(tài)的從Sharded cluster里增加或移除shard。如下所示,cluster目前擁有2個(gè)shard,均為復(fù)制集。
mongos> db.addShard("mongo-9003/10.1.72.135:9003,10.1.72.136:9003,10.1.72.137:9003")
mongos> db.addShard("mongo-9003/10.1.72.135:9003,10.1.72.136:9003,10.1.72.137:9003")
mongos> db.shards.find()
{ "_id" : "mongo-9003", "host" : "mongo-9003/10.1.72.135:9003,10.1.72.136:9003,10.1.72.137:9003" }
{ "_id" : "mongo-9004", "host" : "mongo-9004/10.1.72.135:9004,10.1.72.136:9004,10.1.72.137:9004" }
config.databases
config.databases集合存儲(chǔ)所有數(shù)據(jù)庫(kù)的信息,包括DB是否開啟分片,primary shard信息,對(duì)于數(shù)據(jù)庫(kù)內(nèi)沒有開啟分片的集合,所有的數(shù)據(jù)都會(huì)存儲(chǔ)在數(shù)據(jù)庫(kù)的primary shard上。
如下所示,shtest數(shù)據(jù)庫(kù)是開啟分片的(通過(guò)enableSharding命令),primary shard為mongo-9003; 而test數(shù)據(jù)庫(kù)沒有開啟分片,primary shard為mongo-9003。
mongos> sh.enableSharding("shtest") { "ok" : 1 }
mongos> db.databases.find()
{ "_id" : "shtest", "primary" : "mongo-9003", "partitioned" : true }
{ "_id" : "test", "primary" : "mongo-9003", "partitioned" : false }
Sharded cluster在數(shù)據(jù)庫(kù)創(chuàng)建時(shí),為用戶選擇當(dāng)前存儲(chǔ)數(shù)據(jù)量最小的shard作為數(shù)據(jù)庫(kù)的primary shard,用戶也可調(diào)用movePrimary命令來(lái)改變primary shard以實(shí)現(xiàn)負(fù)載均衡,一旦primary shard發(fā)生改變,mongos會(huì)自動(dòng)將數(shù)據(jù)遷移到的新的primary shard上。
config.colletions
數(shù)據(jù)分片是針對(duì)集合維度的,某個(gè)數(shù)據(jù)庫(kù)開啟分片功能后,如果需要讓其中的集合分片存儲(chǔ),則需調(diào)用shardCollection命令來(lái)針對(duì)集合開啟分片。
如下命令,針對(duì)shtest數(shù)據(jù)里的hello集合開啟分片,使用x字段作為shard key來(lái)進(jìn)行范圍分片。
mongos> sh.shardCollection("shtest.coll", {x: 1})
{ "collectionsharded" : "shtest.coll", "ok" : 1 }
mongos> db.collections.find()
{ "_id" : "shtest.coll", "lastmodEpoch" : ObjectId("57175142c34046c3b556d302"), "lastmod" : ISODate("1970-02-19T17:02:47.296Z"), "dropped" : false, "key" : { "x" : 1 }, "unique" : false }
config.chunks
集合分片開啟后,默認(rèn)會(huì)創(chuàng)建一個(gè)新的chunk,shard key取值[minKey, maxKey]內(nèi)的文檔(即所有的文檔)都會(huì)存儲(chǔ)到這個(gè)chunk。當(dāng)使用Hash分片策略時(shí),可以預(yù)先創(chuàng)建多個(gè)chunk,以減少chunk的遷移。
mongos> db.chunks.find({ns: "shtest.coll"})
{ "_id" : "shtest.coll-x_MinKey", "ns" : "shtest.coll", "min" : { "x" : { "$minKey" : 1 } }, "max" : { "x" : { "$maxKey" : 1 } }, "shard" : "mongo-9003", "lastmod" : Timestamp(1, 0), "lastmodEpoch" : ObjectId("5717530fc34046c3b556d361") }
當(dāng)chunk里寫入的數(shù)據(jù)量增加到一定閾值時(shí),會(huì)觸發(fā)chunk分裂,將一個(gè)chunk的范圍分裂為多個(gè)chunk,當(dāng)各個(gè)shard上chunk數(shù)量不均衡時(shí),會(huì)觸發(fā)chunk在shard間的遷移。如下所示,shtest.coll的一個(gè)chunk,在寫入數(shù)據(jù)后分裂成3個(gè)chunk。
mongos> use shtest
mongos> for (var i = 0; i < 10000; i++) { db.coll.insert( {x: i} ); }
mongos> use config
mongos> db.chunks.find({ns: "shtest.coll"})
{ "_id" : "shtest.coll-x_MinKey", "lastmod" : Timestamp(5, 1), "lastmodEpoch" : ObjectId("5703a512a7f97d0799416e2b"), "ns" : "shtest.coll", "min" : { "x" : { "$minKey" : 1 } }, "max" : { "x" : 1 }, "shard" : "mongo-9003" }
{ "_id" : "shtest.coll-x_1.0", "lastmod" : Timestamp(4, 0), "lastmodEpoch" : ObjectId("5703a512a7f97d0799416e2b"), "ns" : "shtest.coll", "min" : { "x" : 1 }, "max" : { "x" : 31 }, "shard" : "mongo-9003" }
{ "_id" : "shtest.coll-x_31.0", "lastmod" : Timestamp(5, 0), "lastmodEpoch" : ObjectId("5703a512a7f97d0799416e2b"), "ns" : "shtest.coll", "min" : { "x" : 31 }, "max" : { "x" : { "$maxKey" : 1 } }, "shard" : "mongo-9004" }
config.settings
config.settings集合里主要存儲(chǔ)sharded cluster的配置信息,比如chunk size,是否開啟balancer等
mongos> db.settings.find()
{ "_id" : "chunksize", "value" : NumberLong(64) }
{ "_id" : "balancer", "stopped" : false }
其他集合
●config.tags主要存儲(chǔ)sharding cluster標(biāo)簽(tag)相關(guān)的你洗,以實(shí)現(xiàn)根據(jù)tag來(lái)分布chunk的功能;
●config.changelog主要存儲(chǔ)sharding cluster里的所有變更操作,比如balancer遷移chunk的動(dòng)作就會(huì)記錄到changelog里;
●config.mongos存儲(chǔ)當(dāng)前集群所有mongos的信息;
●config.locks存儲(chǔ)鎖相關(guān)的信息,對(duì)某個(gè)集合進(jìn)行操作時(shí),比如moveChunk,需要先獲取鎖,避免多個(gè)mongos同時(shí)遷移同一個(gè)集合的chunk。
來(lái)源:數(shù)據(jù)庫(kù)內(nèi)核月報(bào)
原文:http://mysql.taobao.org/monthly/2016/05/08/
如有侵權(quán)或不周之處,敬請(qǐng)勞煩聯(lián)系若飛(微信:1321113940)馬上刪除,謝謝!