MongoDB sharding chunk 分裂與遷移詳解
MongoDB Sharding
關於 MongoDB sharding 的原理,如果不了解請先參考
注:本文的內容基於 mongoDB 3.2 版本。
Primary shard
使用 MongoDB sharding 後,數據會以 chunk 為單位(默認64MB)根據 shardKey
分散到後端1或多個 shard 上。
每個 database 會有一個 primary shard
,在數據庫創建時分配
- database 下啟用分片(即調用
shardCollection
命令)的集合,剛開始會生成一個[minKey, maxKey] 的 chunk,該 chunk 初始會存儲在primary shard
上,然後隨著數據的寫入,不斷的發生 chunk 分裂及遷移,整個過程如下圖所示。 - database 下沒有啟用分片的集合,其所有數據都會存儲到
primary shard
何時觸發 chunk 分裂?
mongos 上有個 sharding.autoSplit
的配置項,可用於控製是否自動觸發 chunk 分裂,默認是開啟的。如無專業人士指導,強烈建議不要關閉 autoSplit,更好的方式是使用「預分片」的方式來提前分裂,後麵會詳細介紹。
mongoDB 的自動 chunk 分裂隻會發生在 mongos 寫入數據時,當寫入的數據超過一定量時,就會觸發 chunk 的分裂,具體規則如下。
int ChunkManager::getCurrentDesiredChunkSize() const {
// split faster in early chunks helps spread out an initial load better
const int minChunkSize = 1 << 20; // 1 MBytes
int splitThreshold = Chunk::MaxChunkSize; // default 64MB
int nc = numChunks();
if (nc <= 1) {
return 1024;
} else if (nc < 3) {
return minChunkSize / 2;
} else if (nc < 10) {
splitThreshold = max(splitThreshold / 4, minChunkSize);
} else if (nc < 20) {
splitThreshold = max(splitThreshold / 2, minChunkSize);
}
return splitThreshold;
}
bool Chunk::splitIfShould(OperationContext* txn, long dataWritten) const {
dassert(ShouldAutoSplit);
LastError::Disabled d(&LastError::get(cc()));
try {
_dataWritten += dataWritten;
int splitThreshold = getManager()->getCurrentDesiredChunkSize();
if (_minIsInf() || _maxIsInf()) {
splitThreshold = (int)((double)splitThreshold * .9);
}
if (_dataWritten < splitThreshold / ChunkManager::SplitHeuristics::splitTestFactor)
return false;
if (!getManager()->_splitHeuristics._splitTickets.tryAcquire()) {
LOG(1) << "won't auto split because not enough tickets: " << getManager()->getns();
return false;
}
......
}
chunkSize 為默認64MB是,分裂閾值如下
集合 chunk 數量 | 分裂閾值 |
---|---|
1 | 1024B |
[1, 3) | 0.5MB |
[3, 10) | 16MB |
[10, 20) | 32MB |
[20, max) | 64MB |
寫入數據時,當 chunk 上寫入的數據量,超過分裂閾值時,就會觸發 chunk 的分裂,chunk 分裂後,當出現各個 shard 上 chunk 分布不均衡時,就會觸發 chunk 遷移。
何時觸發 chunk 遷移?
默認情況下,MongoDB 會開啟 balancer,在各個 shard 間遷移 chunk 來讓各個 shard 間負載均衡。用戶也可以手動的調用 moveChunk
命令在 shard 之間遷移數據。
Balancer 在工作時,會根據shard tag
、集合的 chunk 數量
、shard 間 chunk 數量差值
來決定是否需要遷移。
(1)根據 shard tag 遷移
MongoBD sharding 支持 shard tag
特性,用戶可以給 shard 打上標簽,然後給集合的某個range 打上標簽,mongoDB 會通過 balancer 的數據遷移來保證「擁有 tag 的 range 會分配到具有相同 tag 的 shard 上」。
(2)根據 shard 間 chunk 數量遷移
int threshold = 8;
if (balancedLastTime || distribution.totalChunks() < 20)
threshold = 2;
else if (distribution.totalChunks() < 80)
threshold = 4;
集合 chunk 數量 | 遷移閾值 |
---|---|
[1, 20) | 2 |
[20, 80) | 4 |
[80, max) | 8 |
針對所有啟用分片的集合,如果 「擁有最多數量 chunk 的 shard」 與 「擁有最少數量 chunk 的 shard」 的差值超過某個閾值,就會觸發 chunk 遷移; 有了這個機製,當用戶調用 addShard
添加新的 shard,或者各個 shard 上數據寫入不均衡時,balancer 就會自動來均衡數據。
(3)removeShard 觸發遷移
還有一種情況會觸發遷移,當用戶調用 removeShard
命令從集群裏移除shard時,Balancer 也會自動將這個 shard 負責的 chunk 遷移到其他節點,因 removeShard
過程比較複雜,這裏先不做介紹,後續專門分析下 removeShard
的實現。
chunkSize 對分裂及遷移的影響
MongoDB 默認的 chunkSize 為64MB,如無特殊需求,建議保持默認值;chunkSize 會直接影響到 chunk 分裂、遷移的行為。
- chunkSize 越小,chunk 分裂及遷移越多,數據分布越均衡;反之,chunkSize 越大,chunk 分裂及遷移會更少,但可能導致數據分布不均。
- chunkSize 太小,容易出現 jumbo chunk(即shardKey 的某個取值出現頻率很高,這些文檔隻能放到一個 chunk 裏,無法再分裂)而無法遷移;chunkSize 越大,則可能出現 chunk 內文檔數太多(chunk 內文檔數不能超過 250000 )而無法遷移。
- chunk 自動分裂隻會在數據寫入時觸發,所以如果將 chunkSize 改小,係統需要一定的時間來將 chunk 分裂到指定的大小。
- chunk 隻會分裂,不會合並,所以即使將 chunkSize 改大,現有的 chunk 數量不會減少,但 chunk 大小會隨著寫入不斷增長,直到達到目標大小。
如何減小分裂及遷移的影響?
mongoDB sharding 運行過程中,自動的 chunk 分裂及遷移如果對服務產生了影響,可以考慮一下如下措施。
(1)預分片提前分裂
在使用 shardCollection
對集合進行分片時,如果使用 hash 分片,可以對集合進行「預分片」,直接創建出指定數量的 chunk,並打散分布到後端的各個 shard。
指定 numInitialChunks
參數在 shardCollection
指定初始化的分片數量,該值不能超過 8192。
Optional. Specifies the number of chunks to create initially when sharding an empty collection with a hashed shard key. MongoDB will then create and balance chunks across the cluster. The numInitialChunks must be less than 8192 per shard. If the collection is not empty, numInitialChunks has no effect.
如果使用 range 分片,因為 shardKey 的取值不確定,預分片意義不大,很容易出現部分 chunk 為空的情況,所以 range 分片隻支持 hash 分片。
(2)合理配置 balancer
monogDB 的 balancer 能支持非常靈活的配置策略)來適應各種需求
- Balancer 能動態的開啟、關閉
- Blancer 能針對指定的集合來開啟、關閉
- Balancer 支持配置時間窗口,隻在製定的時間段內進行遷移
參考資料
- Aliyun MongoDB sharding
- Manage Sharded Cluster Balancer
- shardCollection command
- Migration Thresholds
- shard tag
最後更新:2017-05-31 11:31:50