mongodb分片片鍵的選擇(持續更新中)


首先要了解項目的情況,檢查使用情況

對集合進行分片時,要選擇一個或者兩個字段拆分數據,這個鍵叫做片鍵 一旦擁有對個分片,在修改片鍵幾乎是不肯能的事情,因此選擇合適的片鍵是非常重要的.
對集合分片之前要問自己集合問題
  1. 計划做多少分片`?擁有三個分片的集群要比1000個的更具有靈活性,隨着集群變得越來越大 不應做那些需要查詢所有分片的查詢,因此幾乎所有查詢都需包含片鍵
  2. 分片是為了減少讀寫延遲么?延遲就是某個操作花費的時間.降低寫延遲的方式通常是將請求發送到地理位置更近的服務器或者更強大的機器上
  3. 分片是為了增加讀寫吞吐量么? 吞吐量是指集群在同一時間能夠處理的請求數量,增加吞吐量通常需要提高並行性,並確保請求被均衡的分布到各集群成員上
  4. 分片是為了增加系統資源么?如果是這樣 可能會希望盡量保持工作集較小
根據這些問題來對不用的片鍵進行評估,並判斷所選的片鍵是否適用於自己的情況,這樣做能夠提供所需要的目標查詢么?能夠按所需方式提高系統吞吐量或者減少讀寫延遲么?如需保持工作集的小巧,這樣做可以打到要求么?
 
升序片鍵
升序片鍵 有點類似於date字段或者objectId 是一種隨着時間穩定增長的字段,自增長的主鍵是升序鍵的另一個例子.假如我們依據升序鍵做片鍵 如使用objectId的集合中的"_id"鍵 如果基於_id分片 那么集合會根據不同的_id范圍被拆分為多個塊,
假設要創建一個新的文檔 會插入哪個塊呢?答案是范圍最接近$maxKey的快 也就是最大塊. 這樣每次添加的文檔都會出現這個塊中,這樣會帶來需要不量的屬性 首先所有的寫請求都會被路由到找這個分片中,該快是唯一一個不斷增長和拆分的塊,因為只有他接收寫請求,隨着數據的不斷插入 當達到拆分閥值的時候  mongos 就會對該塊進行拆分 所以該塊不斷拆分出小塊
這種模式進程導致mongodb的數據均衡處理變得更為困難,因為所有的新快都死由同一個分片創建的,因此必須不斷的將一些快移至其他分片,而不能像在一個比較均衡發布的系統中那樣 只需要糾正那些比較小的不均衡就好了
隨機分發的片鍵
隨機分發的鍵可以是用戶名 郵件地址 udid(唯一設備標識符) md5散列值 或者是數據集中其他一些沒有規律的鍵 隨着數據的不斷插入 數據的隨機性一位置 新插入的數據會比較均衡的分布在不同的塊中,由於寫入數據是隨機分發的 各個分片增長的速度應該大致相同,這就減少了需要進行遷移的次數
隨機分發片鍵的唯一弊端在於:mongodb在隨機訪問超出ram大小的數據時效率不高.如果擁有足夠多的ram或者不介意系統性能的話 使用隨機片鍵在集群上分配負載時非常好的
基於位置的片鍵
可以是用戶的ip 經緯度 或者地址 位置片鍵不必也實際的物理位置字段相關 這里的位置比較抽象 數據會依據這個位置進行分組.無論如何 所有與該鍵值比較接近的文檔都會保存在同一范圍的塊中,這樣可以比較方便的將數據與相應的用戶, 以及相關聯的數據保存在一起
假如我們有一個集合文檔按照IP地址進行分片 文檔會依據IP地址被分成不同的塊,冰水機分布在集群中
 如果希望特定范圍的塊出現在特定分片中  可以在分片中添加tag 然后為塊指定相應的tag
sh.addShardTag("shard0000":"USPS")
sh.addShardTag("shard0001":"Apple")
sh.addShardTag("shard0002":"Apple")
然后創建下列規則 
sh.addTagRange("test.ips",{"ip":"056.000.000.000"},{"ip":"057.000.000.000"},"USPS")
sh.addTagRange("test.ips",{"ip":"017.000.000.000"},{"ip":"018.000.000.000"},"Apple")
均衡器在移動塊時,會試圖將這些范圍的塊移動到這些分片上,注意該過程不會立即生效,沒有被打過標簽的塊仍會正常移動.
 片鍵策略
 
散列片鍵
 
如果追求的是數據加載速度的極致,那么散列片鍵是最佳選擇,散列片鍵可使其他任何鍵隨機分發.因此打算在大量查詢中使用升序鍵但同時又希望寫入數據隨機分發的話,散列片鍵會是非常好的選擇.
弊端 是無法使用散列片鍵做指定目標的范圍查詢.
創建一個散列片鍵 首先要創建散列索引 db.users,ensureIndex({"username":"hashed"}) 然后對集合分片 sh.shardConllection("app.users",{"username":"hashed"})
使用散列片鍵存在着一定的局限性.首先不能使用unique選項.其次,與其他片鍵一樣,不能使用數組字段.最后注意,浮點型的值會先被取整,然后才會進行散列,所以1和1.999會得到相同的散列值
 
流水策略
 
如果有一些服務器比其他服務器更強大,我們會希望讓這些強大的服務器處理更多負載,比如說 一個使用SSD的分片能夠處理10被與其他機器的負載.幸運的是,我們有10個其他分片.可強制將所有新數據插入到SSD,然后讓均衡器將舊的塊移動到其他分片上 這樣能夠提供比轉式磁盤更低的延遲
 
為了實現這個策略 需將最大范圍的塊分布在SSD上  為SSD指定一個標簽  sh.addShardTag("shard-name","ssd")
將升序鍵的當前值一直到正無窮范圍的塊指定分布在SSD分片上,以便后續也日請求均被發到SSD分片上 sh.addTagRange("dbname.collName",{"_id":ObjectId()},{"_id":MaxKey},"ssd")
現在 所有插入的請求俊輝被路由到這個塊上,這個塊始終位於標簽為ssd的分片上.
 
但是 除非修改標簽范圍,否則從升序鍵的當前值一直到正無窮的這個范圍則被固定在了這回分片上  可以創建一個定時任務每天更新一次標簽范圍 如下
use config
var tag = db.tags.findOne{{"ns":"dbName.collName","max" : {"shardKey": MaxKey }}} // 獲得擁有最大key的塊 
tag.min.shardKey = ObjectId() //修改該塊的最小鍵的值 為當前的objectId
db.tags.save(tag) //保存
這樣前一天的塊就可以被移動到其他分片上了
此策略的另一個弊端是 需要做一些修改才能進行拓展 如果寫請求超出了SSD的處理能力 想要將負載均衡的分不到當前服務器和另一台服務器並不簡單
 
如果沒有高性能的服務器來處理插入流水 或者沒有使用標簽 那么就不要將升序鍵用作片鍵,否則所有請求都會被路由到同意分片上.
 
多熱點
 
單個mongod服務器在處理升序寫請求時是最有效的,他和分片相沖突 寫請求均勻分布在集群中 分片才是最高效的.這種技術會創建多個熱點(最好每個分片都創建幾個熱點) 寫請求於是會均衡的分布在集群內 而單個分片上則是以升序分布的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM