閱讀506 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

_2017_05_30_7_11_09

何時觸發 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 支持配置時間窗口,隻在製定的時間段內進行遷移

參考資料

最後更新:2017-05-31 11:31:50

  上一篇:go  數學與軟件工程那些令人驚訝的相似性
  下一篇:go  生活在全息宇宙中是一種什麼樣的體驗?