MongoDB自動分片介紹
高性能、易擴展一直是MongoDB的立足之本,同時規(guī)范的文檔和接口更讓其深受用戶喜愛塑猖,這一點從分析DB-Engines的得分結(jié)果不難看出——僅僅1年時間朴皆,MongoDB就完成了第7名到第五名的提升缘薛,得分就從124分上升至214分,上升值是第四名PotgreSQL的兩倍埃儿,同時當(dāng)下與PostgreSQL的得分也只相差16分不到。
1. 片鍵介紹
數(shù)據(jù)劃分(partitioning)關(guān)鍵問題是怎么樣將一個集合中的數(shù)據(jù)均衡的分布在集群中的節(jié)點上融涣。 MongoDB 數(shù)據(jù)劃分的是在集合的層面上進行的童番,它根據(jù)片鍵來劃分集合中的數(shù)據(jù)。
(1)使用片鍵的取值范圍指定數(shù)據(jù)塊
設(shè)置分片的時候威鹿,需要從集合里選出一個字段剃斧,用該字段的值作為數(shù)據(jù)拆分的依據(jù),這個字段稱為片鍵(shard key)忽你,文檔中的數(shù)據(jù)按照這個字段排序切分成塊幼东,分布到各個片上。比如說有個表示人員的集合科雳,如果選擇名字(“name”)字段作為片鍵根蟹,第一片可能會存放名字以AF開頭的文檔,第二個存放的是以GP開頭的文檔糟秘,第三個存的Q~Z的名字简逮。隨著添加(刪除)片,MonogDB會重新平衡數(shù)據(jù)尿赚,使每片的流量都比較均衡散庶,數(shù)據(jù)量也在合理范圍內(nèi)蕉堰。
按照片鍵取值范圍來作為數(shù)據(jù)塊劃分的區(qū)間依據(jù),優(yōu)點是按范圍查詢的時候它的效率很高悲龟,當(dāng)給定一個查詢范圍屋讶,根據(jù)mongos中的映射表可以很快的定位到分片上的數(shù)據(jù)塊。除此之外當(dāng)兩個分片的鍵取值比較靠近的時候须教,會被放到相近的塊中丑婿,由于數(shù)據(jù)的局部性原理,這樣的話可以加快查詢效率没卸,同時也可以減少內(nèi)存換頁次數(shù)羹奉。
缺點是可能會導(dǎo)致數(shù)據(jù)分布不均衡,如果選擇的片鍵具有線性的性質(zhì)约计,例如時間诀拭,將其作為片鍵的話 ,在某個時間段的寫請求(讀請求)都會被映射到同一個分片的同一個數(shù)據(jù)塊上煤蚌, 這樣的話不僅會降低系統(tǒng)的讀寫性能耕挨,而且也會因?qū)懖僮鬟^于集中導(dǎo)致片間的不平衡。
注意:對于升序片鍵尉桩,要分析數(shù)據(jù)分布性問題筒占,首先我們要記住分片是基于范圍的。使用升序的片鍵蜘犁,所有最新插入的數(shù)據(jù)都會落到某個很小的連續(xù)范圍內(nèi)翰苫。也就是說,這些插入都會被路由到一個塊上这橙,而這個塊肯定存在某個片上奏窑,這實際上抵消了分片一個很大的好處,即將插入的負載自動分布到不同的機器上屈扎,這對插入負載很高的應(yīng)用是不合理的埃唯。而且,mongodb是帶平衡器的鹰晨,如果某個片上的chunk過多墨叛,那么平衡器會將多出的chunk轉(zhuǎn)移到其他片,升序片鍵其實也加重了轉(zhuǎn)移chunk的負擔(dān)模蜡。注意漠趁,升序片鍵并不影響更新,只要是隨機更新的就可以哩牍。再想象這樣一種情況棚潦,在最初的時候,整個分片里只有一個塊膝昆,例如1到10000丸边,當(dāng)數(shù)據(jù)量增長至20000的時候叠必,則該分片被分成兩個數(shù)據(jù)塊,然后將10000到20000的數(shù)據(jù)塊遷移到分片2上妹窖,而之后纬朝,所有的寫入操作都是寫入到分片2中。這就造成了熱點全部集中到了分片2上骄呼,而人工造成了不平衡的情況共苛。
(2)按照片鍵哈希值來作為數(shù)據(jù)塊的劃分區(qū)間依據(jù)
優(yōu)點是可以確保一個比較均衡的數(shù)據(jù)分布,因為即使當(dāng)兩個文檔的片鍵取值很接近的時候蜓萄,例如上面例子中一個x=25隅茎,一個x=26,它們的哈希結(jié)果也會有很大的差別嫉沽,這樣的話數(shù)據(jù)會隨機的分布到集群中辟犀,有利于數(shù)據(jù)的均衡的分布,減少數(shù)據(jù)塊的移動次數(shù)绸硕,同時由于數(shù)據(jù)分散會減少單個數(shù)據(jù)塊的寫操作的壓力堂竟,提高寫入速度。
缺點是隨機劃分導(dǎo)致數(shù)據(jù)過于分散玻佩,當(dāng)要查詢某個范圍內(nèi)的數(shù)據(jù)時比如年齡大于20小于25的所有男生信息出嘹,如果直接使用范圍劃分的話,由于其具有良好的數(shù)據(jù)局部性特點咬崔,可能只要訪問幾個相鄰的數(shù)據(jù)塊就行了税稼, 但是如果要使用哈希劃分的方法很可能要訪問所有的數(shù)據(jù)塊。
注意對于像哈希這種完全隨機片鍵刁赦,可以防止數(shù)據(jù)過度集中的分布性問題娶聘,有效減輕單個片的插入負載。但這并不完全合理甚脉。假設(shè)分片集合里的每個文檔都包含一個MD5,而且MD5就是分片鍵铆农,在對每個分片的MD5字段索引進行插入的時候牺氨,每次插入過程中,索引中的每個虛擬內(nèi)存分頁都有可能被訪問到墩剖,實際上這就意味著索引必須總是裝在內(nèi)存里猴凹,如果索引和數(shù)據(jù)不斷增多,超出了物理內(nèi)存的限制岭皂,那么就會產(chǎn)生頁錯誤(page fault)郊霎,導(dǎo)致性能下降。
這其實是局部引用性問題爷绘。局部的概念书劝,在這里指任意給定時間間隔內(nèi)所訪問的數(shù)據(jù)基本都是有關(guān)系的进倍,例如雖然升序的片鍵是糟糕的,但是它提供了很好的局部性购对,對索引的連續(xù)插入都會發(fā)生在最近使用的虛擬內(nèi)存分頁里猾昆;因此,在任意時刻內(nèi)存里只要有一部分索引就可以了骡苞。
再用上邊的例子垂蜗,比如說MD5是用戶存的文件的MD5值,作為片鍵解幽,用戶上傳100個文件贴见,那么對索引的修改就基本會發(fā)生在隨機的100個地方,但是如果我們使用用戶ID作為片鍵躲株,那么每次寫索引基本都會發(fā)生在同一個地方片部,因為插入的文檔都擁有相同的用戶ID值。這就利用了局部性徘溢。
完全隨機片鍵還有一個問題吞琐,對這個片鍵任意一個有意義的范圍查詢,都會被發(fā)送到所有的分片上然爆,然后返回mongos匯總站粟。但是對一個較粗粒度的片鍵進行范圍查詢,是可以落到單個分片上曾雕。
(3)取值有限的片鍵
這是一種粗力度的片鍵奴烙,比如上邊說的用戶ID。如果按照用戶ID分片剖张,你可以預(yù)料到插入會分布在各個分片上切诀,因為無法預(yù)知哪個用戶何時會插入數(shù)據(jù)。這樣一來搔弄,粗粒度分片鍵也能擁有隨機性幅虑,還能發(fā)揮分片集群的優(yōu)勢。而且粗粒度的片鍵還能使用局部性帶來的效率提升顾犹。當(dāng)某個用戶上傳100個文件倒庵,基于用戶ID字段的分片建能確保這些插入都落到同一個分片上,并幾乎能寫入索引的同一部分炫刷,這樣效率很高擎宝。粗粒度分片鍵在分布性和局部性上都表現(xiàn)很好,但是它也有一個很難解決的問題:塊有可能無限制的增長浑玛。想想基于用戶ID的片鍵绍申,假如有幾個特殊用戶,他們上傳了上百萬個文件,那么一個塊里就可能只有一個用戶ID极阅,這個塊能拆分么胃碾?不能,因為用戶ID是最小的粒度涂屁,拆分了查詢就沒法路由到數(shù)據(jù)书在。這就造成分片之間數(shù)據(jù)量不均衡。更典型的就是type拆又,status這類的字段儒旬,因為它們的選擇性實在是太低,導(dǎo)致無法拆分帖族。片鍵基比較小時栈源,所有的鍵值相同導(dǎo)致MongoDB不能分裂Chunk,遷移這些不可分裂的Chunk將更加耗時竖般,即使遷移后也難以保證數(shù)據(jù)在各個分片上的平衡甚垦。Chunk數(shù)量被基約束住后,我們就不能利用MongoD分片集群特性將集合部署到更多的機器涣雕。
2. 片鍵的選取原則
在Sharding結(jié)構(gòu)中艰亮,分片策略,片鍵選擇是影響性能的關(guān)鍵因素挣郭,片鍵不僅影響數(shù)據(jù)分布迄埃,而且影響業(yè)務(wù)邏輯,所以片鍵的選擇不單單是均勻的將數(shù)據(jù)分布到各個片上兑障,而且要考慮查詢的性能侄非。壞的片鍵有時候會導(dǎo)致數(shù)據(jù)分布很差,有時候會導(dǎo)致無法使用局部性原理流译,還有一些會影響數(shù)據(jù)塊的拆分逞怨。
上邊我們討論了低效片鍵的問題和原因,理想的片鍵應(yīng)該結(jié)合粗粒度分片鍵與細粒度片鍵兩者的優(yōu)勢福澡。
一個好的片鍵必須包含的特性:
1叠赦、保證CRUD能利用局限性 ==》升序片鍵的優(yōu)點
2、將插入數(shù)據(jù)均勻分布到各個分片上 ==》隨機片鍵的優(yōu)點
3革砸、有足夠的粒度進行塊拆分 ==》粗粒度片鍵的優(yōu)點
滿足這些要求的的片鍵通常由兩個字段組成眯搭,第一個是粗粒度,第二個是粒度較細业岁。那么我們需要使用復(fù)合片鍵。例如對上面的例子寇蚊,選取{userid:1笔时,_id:1}作為片鍵,當(dāng)用戶同時插入數(shù)據(jù)時仗岸,我們可以預(yù)見大多數(shù)情況下允耿,這些數(shù)據(jù)會被均勻的分布到所有的片上借笙,而且分片里的唯一字段_id能保證對任意一個文檔的查詢和更新始終都能指向單個分片。如果對用戶ID執(zhí)行更復(fù)雜的查詢较锡,那么路由也只會將查詢路由包含此用戶ID存在的片上业稼,而不會發(fā)到所有分片。由于_id(升序)的存在蚂蕴,保證了塊始終是能繼續(xù)拆分的低散,哪怕用戶創(chuàng)建了大量文檔,情況也是如此骡楼。
所以在選擇片鍵時盡量能保持良好的數(shù)據(jù)局部性而又不會導(dǎo)致過度熱點的出現(xiàn)熔号,很多時候,組合片鍵是一種比較常用的做法鸟整。
除此之外引镊,也可以選擇我們經(jīng)常查詢的字段作為片鍵,這類分片鍵可以使得查詢時mongos僅僅將查詢發(fā)送給特定的mongod實例篮条,不需要等待多個實例返回數(shù)據(jù)后再進行合并弟头。