1. 程式人生 > >mongodb分片時的片鍵選擇

mongodb分片時的片鍵選擇

記住這個原則

片建選擇【必須有索引,推薦組合鍵,鍵值的變化範圍大的】

當MongoDB整個架構已經部署好以後,真正考驗架構者能力的時候就到了:該如何選擇片鍵。



如果選擇了一個不恰當的片鍵,他可能會在訪問量變大的時候,使你的整個應用系統崩潰,同樣好的片鍵可以構成一個良性的生態系統,根據需要增刪伺服器,MongoDB會確保系統一直正確的執行下去。


咱們先看看幾種不恰當的片鍵


1,小基數片鍵


    假設我們有一個儲存使用者資訊的應用程式,每個文件有一個continent的欄位,儲存使用者所在地區,其值有:africa,antarctica,asia,australia,europe,north america,south america。考慮到我們在每個州都有一個數據中心,並且想從人們所在地的資料中心為其提供資料,我們決定按該欄位分片。


     集合開始於某個資料中心的一個分片的初始塊(-∞,∞),所有的插入和讀取都落在這一塊上,一旦他變得足夠大,就會被分成兩個塊(-∞,europe)和[europe,∞),這樣一來,所有來自africa,antarctica,asia和australia的文件都會被分到第一塊,其他的都會被分到第二塊,隨著更多的文件被新增到資料庫,集合最終會變成7個塊,如下


   (-∞,antarctica)


   [anrarctica,asia)


   [asia,australia)


   [australia,europe)


   [europe,north america)


   [north america,south america)


   [south america,∞)


然後呢?


MongoDB不能再進一步分割這些塊了,快會越來越大,雖然暫時不會出問題,但是當伺服器硬碟空間被用完的時候,你就沒辦法了,只能購買新的硬碟,悲劇的是硬碟是有極限的。


  由於片鍵數量有限,因此這種片鍵稱為小基數片鍵,如果選擇了一個基數很小的片鍵,到頭來肯定會得到一堆巨大無法移動也不能分割的塊,這時候做維護做擴充套件是什麼感覺,你懂得。


 如果是因為需要在某個欄位上做大量查詢而採用小基數片鍵,那就需要使用組合片鍵了,一個片鍵包含兩個欄位,並確保第二個欄位有非常多不同的值供MongoDB用來進行分割,比如大部分的查詢都和時間關聯,可以用時間欄位做第二個欄位,又可以減輕負載。


2,升序片鍵


從RAM中讀取資料要比磁碟取快,所以目標是儘可能多的訪問記憶體中的資料。一次,如果有些資料總是被一起訪問,我們就希望能夠把他們保持在一起。對大部分應用來說,新資料被訪問的次數總比老的多,所以往往會使用如時間戳或者objectId一類的欄位來分片,但是這並不想我們期望的那樣可行。


 比如我們有一個類似微博的服務,其中每個文件都包含一條訊息,傳送人和傳送時間,我們按時間來分片,讓我們看看MongoDB會如何執行


  首先還是一個大塊(-∞,∞),文件會全部插入到這個分片上,然後開始分裂,比如(-∞,1294516901),[1294516901,-∞)----(使用的是時間戳),由於是從片鍵中點把塊分開,所以在分割開快的那一刻開始,說有資料都會插入到第二個塊上,不會在插入帶第一個塊上,一旦塊二被插滿,他會在分割成兩塊,但同樣的只會在最後一塊插入資料,這種情況會一直持續下去,這就造成了一個單一且不可分散的熱點。在網站高峰期可見這個點上的壓力。


3,隨機片鍵


    有時為了避免熱點,會採用一個取值隨機的欄位來做分片,採用這種片鍵一開始還不錯,但是隨著資料量越來越大,他會越來越慢。


     比如我們在分片集合中儲存照片縮圖,每個文件包含了照片的二進位制資料,二進位制資料的md5雜湊值,以及描述等欄位,我們決定在md5雜湊值上做分片。


    隨著集合的增長,我們最終會得到一組均勻分佈於各分片的資料塊。目前一起正常。現在假設我們非常忙而分片2上的一個塊填滿並分裂了,配置伺服器注意到分片2比分片1多出了10個塊並判定應該抹平分片間的差距,這樣MongoDB就需要隨機載入5個塊的資料到記憶體中併發給片1,考慮到資料序列的隨機性,一般情況下這些資料可能不會出現在記憶體中,所以此時的MongoDB會給RAM帶來更大的壓力,而且還會引發大量的磁碟IO。


   另外,片鍵上必須有索引,因此如果選擇了從不依據索引查詢的隨機鍵,基本上可以說浪費了一個索引,另一方面索引的增加會降低寫操作的速度,所以降低索引量也是非常必要的。


那麼怎麼樣的片鍵才是好的片鍵呢?


從上面的分析可得出一個好的片鍵應該具備良好的資料區域性性,但又不會因為太區域性而導致熱點出現。


1,準升序鍵加搜尋鍵


     許多應用程式都是訪問新資料更頻繁,所以我們希望資料大致按時間排序,但是同時也要均勻分佈,這樣一來既能把我們正在讀寫的資料保持在記憶體中,又可以均衡的分散在叢集中。


     我們可以通過像{coarselyAscending:1,search:1}這樣的組合片鍵來實現,其中coarselyAscending的每個值最好能對應幾十到幾百個資料塊,而search則應當是應用程式通常都會一句其進行查詢的欄位。


  舉個例子:有個分析程式,使用者定期通過他訪問過去一個月的資料,而我們希望能儘量保持資料易於使用,因此可以使用{month:1,user:1}來做分片,現在來說說執行過程


   首先一個大資料塊((-∞,-∞),(∞,∞)),當他被填滿,MongoDB將自動分割成兩塊,比如:


     ((-∞,-∞),("2012-07","susan"))


    [("2012-07","susan"),(∞,∞))


假設現在還是7月,則所有寫操作會被均勻的分佈到兩個塊上。所有使用者名稱小於susan的資料被寫入塊1中,所有大於susan的資料被寫入塊2,然後整個生態系統就良性運行了,等到8月,MongoDB又開始建立2012-08的塊,分佈還是均衡的(這裡不是時時均衡,肯定有個抹平的過程),等到9月,7月的資料無人訪問就開始退出記憶體,不再佔用資源。


 


--------------------------------------------------------------------------------------------------------------------------------------


當然場景不同,應用就不同,上面也只是基本的東西,具體選擇片鍵還應該根據自己的程式來做,選鍵的時候多考慮以下問題。


   1,寫操作是怎麼樣的,有多大?


   2,系統沒小時會寫多少資料,每天呢,高峰期呢


   3,那些欄位是隨機的,那些是增長的


   4,讀操作是怎麼樣的,使用者在訪問那些資料


   5,資料索引做了嗎?應不應該索引呢?


  6,資料總量有多少


 總的來說,在進行分片前,你需要清楚的瞭解你的資料。


 


 以上內容參考了“深入學習MongoDB” 一書,如果需瞭解更多MongoDB優化的知識,這本數絕對值得一讀,這本書還列出了50例MongoDB優化,維護等等需要注意的地方