MongoDB分片集群技術用于解決海量數據的存儲問題,本文介紹MongoDB分片集群相關的常用知識。
什么情況下使用分片集群?
當您遇到如下問題時,可以使用分片集群解決:
存儲容量受單機限制,即磁盤資源遭遇瓶頸。
讀寫能力受單機限制,可能是CPU、內存或者網卡等資源遭遇瓶頸,導致讀寫能力無法擴展。
如何確定shard、mongos數量?
您可以根據以下方法確定shard和mongos的使用數量:
分片集群僅用于解決海量數據的存儲問題,且訪問量不多。例如一個shard能存儲M, 需要的存儲總量是N,那么您的業務需要的shard和mongos數量按照以下公式計算:
numberOfShards = N/M/0.75 (假設容量水位線為75%)
numberOfMongos = 2+(對訪問要求不高,至少部署2個mongos做高可用)
分片集群用于解決高并發寫入(或讀取)數據的問題,但總的數據量很小。即shard和mongos需要滿足讀寫性能需求,例如一個shard的最大QPS為M,一個mongos的最大QPS為Ms,業務需要的總QPS為Q,那么您的業務需要的shard和mongos數量按照以下公式計算:
numberOfShards = Q/M/0.75 (假設負載水位線為75%)
numberOfMongos = Q/Ms/0.75
說明如果分片集群同時解決上述兩個問題,則按照需求更高的指標進行預估。
上述計算方法是基于分片集群中數據和請求都均勻分布的理想情況下進行預估,實際情況下,分布可能并不均勻,為了讓系統的負載盡量均勻,您需要選擇合理的Shard Key。
mongos和mongod的服務能力,需要用戶根據訪問特性來實測得出。
如何選擇Shard Key?
MongoDB分片集群支持的分片策略
范圍分片,支持基于Shard Key的范圍查詢。
哈希分片,能夠將寫入均衡分布到各個shard。
Tag aware sharding,您可以自定義一些chunk的分布規則。
說明原理
sh.addShardTag()
給shard設置標簽A。sh.addTagRange()
給集合的某個chunk范圍設置標簽A,最終MongoDB會保證設置標簽A的chunk范圍(或該范圍的超集)分布設置了標簽A的shard上。
應用場景
將部署在不同機房的shard設置機房標簽,將不同chunk范圍的數據分布到指定的機房。
將服務能力不同的shard設置服務等級標簽,將更多的chunk分散到服務能力更強的shard上去。
注意事項
chunk分配到對應標簽的shard上無法立即完成,而是在不斷insert、update后觸發split、moveChunk后逐步完成的并且需要保證balancer是開啟的。在設置了tag range一段時間后,寫入仍然沒有分布到tag相同的shard上去。
范圍分片和哈希分片無法解決的問題
Shard Key的取值范圍太小,例如將數據中心作為Shard Key,由于數據中心通常不多,則分片效果不好。
Shard Key中某個值的文檔特別多,會導致單個chunk特別大(即 jumbo chunk),會影響chunk遷移及負載均衡。
根據非Shard Key進行查詢、更新操作都會變成scatter-gather查詢,影響效率。
好的Shard Key擁有的特性
key分布足夠離散(sufficient cardinality)
寫請求均勻分布(evenly distributed write)
盡量避免scatter-gather查詢(targeted read)
示例:
場景:某物聯網應用使用MongoDB分片集群存儲海量設備的工作日志。如果設備數量在百萬級別,設備每10秒向MongoDB匯報一次日志數據,日志包含設備ID(deviceId)和時間戳(timestamp)信息。應用最常見的查詢請求是查詢某個設備某個時間內的日志信息。
查詢請求:查詢某個設備某個時間內的日志信息。
(推薦)方案一:組合設備ID和時間戳作為Shard Key,進行范圍分片。
寫入能均分到多個shard。
同一個設備ID的數據能根據時間戳進一步分散到多個chunk。
根據設備ID查詢時間范圍的數據,能直接利用(deviceId,時間戳)復合索引來完成。
方案二: 時間戳作為Shard Key,進行范圍分片。
新的寫入為連續的時間戳,都會請求到同一個分片,寫分布不均。
根據設備ID的查詢會分散到所有shard上查詢,效率低。
方案三: 時間戳作為Shard Key,進行哈希分片。
寫入能均分到多個shard上。
根據設備ID的查詢會分散到所有shard上查詢,效率低。
方案四:設備ID作為Shard Key,進行哈希分片。
說明如果設備ID沒有明顯的規則,可以進行范圍分片。
寫入能均分到多個shard上。
同一個設備ID對應的數據無法進一步細分,只能分散到同一個chunk,會造成jumbo chunk,根據設備ID的查詢只請求到單個shard,請求路由到單個shard后,根據時間戳的范圍查詢需要全表掃描并排序。
關于jumbo chunk及chunk size
MongoDB默認的chunk size為64 MB,如果chunk超過64 MB且不能分裂(假如所有文檔的Shard Key都相同),則會被標記為jumbo chunk,balancer不會遷移這樣的chunk,從而可能導致負載不均衡,應盡量避免。
當出現jumbo chunk時,如果對負載均衡要求不高,并不會影響到數據的讀寫訪問。如果您需要處理,可以使用如下方法:
對jumbo chunk進行split,split成功后mongos會自動清除jumbo標記。
對于不可再分的chunk,如果該chunk已不是jumbo chunk,可以嘗試手動清除chunk的jumbo標記。
說明清除前,您需要先備份config數據庫,避免誤操作導致config庫損壞。
調大chunk size,當chunk大小不超過chunk size時,jumbo標記最終會被清理。但是隨著數據的寫入仍然會再出現jumbo chunk,根本的解決辦法還是合理的規劃Shard Key。
需要調整chunk size(取值范圍為1~1024 MB)的場景:
遷移時I/O負載太大,可以嘗試設置更小的chunk size。
測試時,為了方便驗證效果,設置較小的chunk size。
初始chunk size設置不合理,導致出現大量jumbo chunk影響負載均衡,此時可以嘗試調大chunk size。
將未分片的集合轉換為分片集合,如果集合容量太大,需要(數據量達到T級別才有可能遇到)調大chunk size才能轉換成功。具體方法請參見Sharding Existing Collection Data Size。
關于負載均衡
MongoDB分片集群的自動負載均衡目前是由mongos的后臺線程來做,并且每個集合同一時刻只能有一個遷移任務。負載均衡主要根據集合在各個shard上chunk的數量來決定的,相差超過一定閾值(和chunk總數量相關)就會觸發chunk遷移。
負載均衡默認是開啟的,為了避免chunk遷移影響到線上業務,可以通過設置遷移執行窗口,例如只允許凌晨02:00~06:00期間進行遷移。
use config
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "02:00", stop : "06:00" } } },
{ upsert: true }
)
在進行分片集群備份時(通過mongos或單獨備份ConfigServer和所有shard),需要執行以下命令停止負載均衡,避免備份的數據出現狀態不一致問題。
sh.stopBalancer()
moveChunk歸檔設置
MongoDB 3.0及以前版本的分片集群可能存在停止寫入數據后,數據目錄里的磁盤空間占用還會一直增加的問題。
上述問題是由sharding.archiveMovedChunks
配置項決定的,該配置項在MongoDB 3.0及以前的版本默認為true
。即在moveChunk時,源shard會將遷移的chunk數據進行歸檔設置,當出現問題時,用于恢復。也就是說,chunk發生遷移時,源節點上的空間并沒有釋放出來,而目標節點又占用了新的空間。
在MongoDB 3.2版本,該配置項默認值為false
,默認不會對moveChunk的數據在源shard上歸檔。