MongoDB的分片機制能夠幫助你將你的數(shù)據(jù)庫劃分到多個服務(wù)器鱼辙,通常在生產(chǎn)環(huán)境中可以將數(shù)據(jù)集劃分到多個副本集中。但分片最好在數(shù)據(jù)庫建立早期劃分,因為一旦你的數(shù)據(jù)大于512GB那么分片劃分就不是那么容易了如筛。這受到MongoDB縱向擴展能力的限制。為了實現(xiàn)分片抒抬,你必須向MongoDB指定使用哪個索引作為片鍵杨刨,然后MongoDB會根據(jù)你的設(shè)置將你的數(shù)據(jù)劃分到有著相同片鍵的數(shù)據(jù)塊(Chunk)中。而后這些數(shù)據(jù)塊將根據(jù)片鍵的大致順序分散到副本集中擦剑。
MongoDB不允許插入沒有片鍵的文檔妖胀。但是允許不同文檔的片鍵類型不一樣,MongoDB內(nèi)部對不同類型有一個排序:
null< 數(shù)字< 字符串 < 對象 < 數(shù)組 < 二進制數(shù)據(jù) < objectId < 布爾值 < 日期 < 正則表達式
分片之后數(shù)據(jù)的存放位置依賴于片鍵惠勒,所以合理的選擇片鍵十分重要赚抡。所以對于分片Key的選定直接決定了集群中數(shù)據(jù)分布是否均衡、集群性能是否合理纠屋。
一涂臣、選擇片鍵的要素
1.讀和寫的分布
其中最重要的一點是讀和寫的分布。如果你總是朝一臺機器寫售担,那么這臺機器將會成為寫瓶頸赁遗,則你的集群的寫性能將會降低。這無關(guān)乎你的集群有多少個節(jié)點族铆,因為所有的寫操作都只在一個地方進行岩四。因此,你不應(yīng)該使用單調(diào)遞增的`_id`或時間戳作為片鍵哥攘,這樣將會導致你一直往最后一個副本集中添加數(shù)據(jù)剖煌。
相類似的是如果你的讀操作一直都在同一個副本集上,那么你最好祈求你的任務(wù)能在機器內(nèi)存所能承受的范圍之內(nèi)献丑。通過副本集將讀請求劃分開能夠使你的工作數(shù)據(jù)集大小隨著分片數(shù)線性擴展挤悉。這樣的話你能夠?qū)⒇撦d壓力均分到各臺機器的內(nèi)存和磁盤之上盅视。
2.數(shù)據(jù)塊的大小
其次是數(shù)據(jù)塊的大小汹粤。MongoDB能夠?qū)⒋蟮臄?shù)據(jù)塊劃分成更小的虫腋,但這種情況僅僅在片鍵不同的情況下發(fā)生。如果你有巨量的數(shù)據(jù)文檔都使用了同樣的片鍵妥畏,那么你相應(yīng)的會得到巨大的數(shù)據(jù)塊邦邦。出現(xiàn)巨大塊是非常不好的安吁,不僅僅因為它會導致數(shù)據(jù)的不平均分布,還因為一旦這個數(shù)據(jù)塊的大小超過某個值燃辖,那么你就不能夠在分片之間移動它了鬼店。
3.每個查詢命中的分片數(shù)目
最后一點,如果能夠保證大部分的查詢請求都能夠命中盡可能少的分片那就最好了黔龟。對于一個查詢請求來說妇智,其延遲直接取決于最慢的那個命中服務(wù)器的延遲;所以你命中的分片越少氏身,那么理論上來說查詢將會越快巍棱。這一點并不是硬性的規(guī)定,不過如果能夠做到充分考慮那么應(yīng)該是很有利的蛋欣。因為數(shù)據(jù)塊在分片上的分布僅僅是近似的遵循片鍵的順序航徙,而并不是嚴格的強制指定。
二陷虎、片鍵的設(shè)計
1.Hashed id
作為第一個方案到踏,你可以使用數(shù)據(jù)文檔_id的哈希作為片鍵。
db.events.createIndex({_id: 'hashed'})
這個方案能夠是的讀和寫都能夠平均分布尚猿,并且它能夠保證每個文檔都有不同的片鍵所以數(shù)據(jù)塊能夠很精細窝稿。
似乎還是不夠完美,因為這樣的話對多個文檔的查詢必將命中所有的分片凿掂。雖說如此讹躯,這也是一種比較好的方案了。
2.多租戶混合索引(Multi-tenant compound index)
db.events.createIndex({projectId: 1, _id: 1})
如上不能簡單地使用projectID作為片鍵缠劝,因為那會導致巨大塊的產(chǎn)生,所以我們引入了_id來將大project打散到多個塊中骗灶。這些打散的塊仍舊是索引連續(xù)的惨恭,所以仍然會分布在用一個分片上。
找一個好的片鍵是很難的耙旦,不過這真的只有兩種方案脱羡。如果在應(yīng)用中找不出一個好的聚合鍵,那么對_id做哈希吧免都。如果你能夠找到锉罐,那么將它與`_id`聚合以避免巨大塊的產(chǎn)生。請記住無論你使用何種聚合鍵绕娘,它都需要能夠?qū)⒆x和寫平均分布以充分利用集群中的每個節(jié)點脓规。
三、GridFS片鍵的設(shè)計
根據(jù)需求的不同险领,GridFS有幾種不同的分片方法侨舆∶虢簦基于預(yù)先存在的索引是慣用的分片辦法:
1)“files”集合(Collection)不會分片,所有的文件記錄都會位于一個分片上,高度推薦使該分片保持高度靈活(至少使用由3個節(jié)點構(gòu)成的replica set)。
2)“chunks”集合(Collection)應(yīng)該被分片挨下,并且用索引”files_id:1”熔恢。已經(jīng)存在的由MongoDB的驅(qū)動來創(chuàng)建的“files_id,n”索引不能用作分片Key(這個是一個分片約束,后續(xù)會被修復(fù))臭笆,所以不得不創(chuàng)建一個獨立的”files_id”索引叙淌。使用“files_id”作為分片Key的原因是一個特定的文件的所有Chunks都是在相同的分片上,非常安全并且允許運行“filemd5”命令(要求特定的驅(qū)動)愁铺。
運行如下命令:
> db.fs.chunks.ensureIndex({files_id: 1});
> db.runCommand({ shardcollection : "test.fs.chunks", key : { files_id : 1 }})
{ "collectionsharded" : "test.fs.chunks", "ok" : 1 }
也可使用files_id的哈希作為片鍵鹰霍,{ files_id : "hashed" }
由于默認的files_id是一個ObjectId,files_id將會升序增長帜讲,因此衅谷,GridFS的全部Chunks都會被從一個單點分片上存取。如果寫的負載比較高似将,就需要使用其他的分片Key了获黔,或者使用其它的值(_id)來作為分片Key了。
轉(zhuǎn)至: