在分片集羣模式下, 有一個Balancer的類,它啓動一個名字爲Balancer的後臺線程, 用來保證不同分片之間chunk數的均勻。 每一個Mongos都有自己的Balancer, 不同的Balancer之間通過分佈式鎖來進行控制, 保證某個一時間只有一個Balancer在工作。
先看一下Balancer的定義:
class Balancer : public BackgroundJob {
public:
Balancer();
virtual ~Balancer();
// BackgroundJob定義了該函數, 是Balancer執行任務的函數
virtual void run();
virtual std::string name() const {
return "Balancer";
}
private:
// 標記該Balancer對應的Mongos的ip:port
std::string _myid;
// Balancer開始執行的時間
time_t _started;
// 上一次遷移的chunk的數量
int _balancedLastTime;
// 該類用來決定哪些chunk要遷移
std::unique_ptr<BalancerPolicy> _policy;
}
我們看到整個Balancer::Run是整個遷移函數的入口, 它首先啓動一個Balancer的線程, 然後檢查與config server以及各個shard的網絡是相通的; 接着就進入balance round-loop, 對於每一個round, 首先會做一些校驗, 然後試圖得到分佈式鎖, 驗證metadata是否與config server一致, 若果這些全部都沒有問題, 才正式進行遷移的準備工作, 通過函數Balancer::_doBalanceRound()得到所有需要遷移的chunk列表,左後將candidate chunks 通過MoveChunk command進行遷移工作。
這裏, BalancerPolicy類是一個特殊的類, 它只有一個函數balance(), 來負責按照一定的策略, 進行判斷某個shard是否有需要遷移的chunks。
void Balancer::_doBalanceRound(OperationContext* txn,
ForwardingCatalogManager::ScopedDistLock* distLock,
vector<shared_ptr<MigrateInfo>>* candidateChunks) {
vector<CollectionType> collections;
ShardInfoMap shardInfo;
Status loadStatus = DistributionStatus::populateShardInfoMap(txn, &shardInfo);
// For each collection, check if the balancing policy recommends moving anything around.
for (const auto& coll : collections) {
// Skip collections for which balancing is disabled
const NamespaceString& nss = coll.getNs();
std::vector<ChunkType> allNsChunks;
Status status = grid.catalogManager(txn)->getChunks(txn,
BSON(ChunkType::ns(nss.ns())),
BSON(ChunkType::min() << 1),
boost::none, // all chunks
&allNsChunks,
nullptr);
set<BSONObj> allChunkMinimums;
map<string, vector<ChunkType>> shardToChunksMap;
for (const ChunkType& chunk : allNsChunks) {
allChunkMinimums.insert(chunk.getMin().getOwned());
vector<ChunkType>& chunksList = shardToChunksMap[chunk.getShard()];
chunksList.push_back(chunk);
}
for (ShardInfoMap::const_iterator i = shardInfo.begin(); i != shardInfo.end(); ++i) {
// This loop just makes sure there is an entry in shardToChunksMap for every shard
shardToChunksMap[i->first];
}
DistributionStatus distStatus(shardInfo, shardToChunksMap);
auto statusGetDb = grid.catalogCache()->getDatabase(txn, nss.db().toString());
shared_ptr<DBConfig> cfg = statusGetDb.getValue();
// This line reloads the chunk manager once if this process doesn't know the collection
// is sharded yet.
shared_ptr<ChunkManager> cm = cfg->getChunkManagerIfExists(txn, nss.ns(), true);
shared_ptr<MigrateInfo> migrateInfo(
_policy->balance(nss.ns(), distStatus, _balancedLastTime));
if (migrateInfo) {
candidateChunks->push_back(migrateInfo);
}
}
}
在獲得了candidateChunks之後, 要開始講這些chunks逐個的調用遷移命令:
int Balancer::_moveChunks(OperationContext* txn,
const vector<shared_ptr<MigrateInfo>>& candidateChunks,
const WriteConcernOptions* writeConcern,
bool waitForDelete) {
int movedCount = 0;
for (const auto& migrateInfo : candidateChunks) {
...
shared_ptr<DBConfig> cfg =
uassertStatusOK(grid.catalogCache()->getDatabase(txn, nss.db().toString()));
shared_ptr<ChunkManager> cm = cfg->getChunkManager(txn, migrateInfo->ns);
ChunkPtr c = cm->findIntersectingChunk(txn, migrateInfo->chunk.min);
if (c->getMin().woCompare(migrateInfo->chunk.min) ||
c->getMax().woCompare(migrateInfo->chunk.max)) {
// Likely a split happened somewhere, so force reload the chunk manager
cm = cfg->getChunkManager(txn, migrateInfo->ns, true);
invariant(cm);
c = cm->findIntersectingChunk(txn, migrateInfo->chunk.min);
// moveAndCommit是會調用moveChunk command後端的接口來進行遷移任務
BSONObj res;
if (c->moveAndCommit(txn,
migrateInfo->to,
Chunk::MaxChunkSize,
writeConcern,
waitForDelete,
0, /* maxTimeMS */
res)) {
movedCount++;
continue;
}
...
return movedCount;
}
上述, 是整個遷移任務的整個流程。接下來是在db端MoveChunk command的執行過程。MoveChunkCommand負責執行具體的遷移過程:
-
解析MoveChunkCommand的參數;
-
獲取分佈式鎖以確保元數據的穩定;
-
遷移過程
獲取所有的要遷移的數據的recordID, 以便在遷移過程中進行最少的搜索。在獲取所有的rcordId的時候需要集合所, 之後集合鎖被解鎖, 這爲repair或者compact提供了時機。 注意, 數據修改沒有問題, 因爲我們註冊了過程中的修改。 -
等待知道遷移完成;
-
加鎖 (關鍵區)
a) 更新我的配置, 實際上是加鎖;
b) 遷移結束;
c) 更新配置服務器;
d) 將更改記錄到配置服務器; -
等待本地數據的cursor失效;
-
刪除本地的數據;