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上歸檔。