分片是随机分布在 MongoDB 中的机器上还是按顺序分布?
例如,如果我有一个跨两台机器分片的 MongoDB 集群,并且我的分片键是一个人的名字,那么以“am”开头的名字会出现在第一台机器(或副本集)上,并且以“nz”开头的名字会出现第二台机器?
非常感谢!
一般来说,不,它们不是按顺序分布的。但是,有一种方式可以在正常操作期间发生这种(或至少近似这种)分布。解释:
如果您在单个分片 (az) 上拥有所有数据,然后添加第二个分片,则平衡器将通过迁移块将数据重新分配到新分片。
平衡器选择要迁移的块的方式通常非常简单,它将选择具有最多块(例如“a”块)的分片上最低的可用范围,并将其移动到具有最少数量的分片大块。冲洗并重复,直到块平衡。
因此,如果您将所有数据放在一个分片上并添加第二个分片,则低范围块 (am) 都将从原始分片移动到新分片,您最终可以得到这种几乎有序的分布.
在您的示例中,您最终会得到一个奇怪的均匀分布的数据,并没有造成真正的伤害。但是,从性能角度来看,对于跨大部分数据的基于范围的查询可能存在问题。考虑一个按顺序遍历索引的查询 - 它一次只会访问一个分片(新分片,然后是旧分片),而不是使用两者的资源来更好地分配数据。
在其他情况下,这种行为会导致数据分布特别差。假设您有一个基于时间的分片键(由于各种原因不好,但仍然很常见)并且您定期删除旧数据。结合上面的场景,新的分片最终将没有数据(低范围,旧,块中的数据都将被删除)。请记住,就平衡而言,一个空块(无数据)与 60MB 块一样多(平衡器纯粹是查看每个分片上的块数,而不是其中的数据量)。
此外,如果您有 2 个分片开始然后插入数据,这一切都不会发生,因为拆分和迁移通常会更随机且分布更广。MongoDB 还将尝试移动最大块以帮助实现这种分布。
总而言之 - 如果您发现了这种类型的分布问题,您可以通过在自己周围移动块来采取措施来修复它。看一看moveChunk 命令(这通常是一个好主意,但不是必需的,在您执行此操作时关闭平衡器,否则您可能无法获得所需的锁)。
查看所有这些的最简单方法是运行sharding status 命令并传递true,即 的参数sh.status(true)。这将打印所有分片集合的详细信息,块所在的位置等。它从配置数据库中提取其信息并使用简单的 Map Reduce 来聚合信息。
有了这些信息,您还可以检查更改日志集合以确定已迁移的内容以及迁移的时间(这也将记录在您的主mongod日志中,但在繁忙的系统上可能难以解析)。
最后,我意识到这有点超出了这个答案的范围,但我已经编写了几个 Javascript 函数(您可以从 MongoDB shell 使用它们),可以让您确定给定集合的分片上的对象和数据分布,其他地方不容易获得的东西。希望你会发现它们很有用:)
请注意:这些功能可能需要大量运行,因此请在生产环境中谨慎使用。
第一个函数以 CSV 格式打印给定命名空间的块信息。它使用命令中的estimate选项datasize来使用计数(而不是扫描所有对象)来计算每个块的大小。删除estimate选项将给出一个更准确的结果(特别是如果你的文档大小而异),但如果数据集不是在RAM中,可以非常耗费资源。
AllChunkInfo = function(ns){
var chunks = db.getSiblingDB("config").chunks.find({"ns" : ns}).sort({min:1}); //this will return all chunks for the ns ordered by min
//some counters for overall stats at the end
var totalChunks = 0;
var totalSize = 0;
var totalEmpty = 0;
print("ChunkID,Shard,ChunkSize,ObjectsInChunk"); // header row
// iterate over all the chunks, print out info for each
chunks.forEach(
function printChunkInfo(chunk) {
var db1 = db.getSiblingDB(chunk.ns.split(".")[0]); // get the database we will be running the command against later
var key = db.getSiblingDB("config").collections.findOne({_id:chunk.ns}).key; // will need this for the dataSize call
// dataSize returns the info we need on the data, but using the estimate option to use counts is less intensive
var dataSizeResult = db1.runCommand({datasize:chunk.ns, keyPattern:key, min:chunk.min, max:chunk.max, estimate:true});
// printjson(dataSizeResult); // uncomment to see how long it takes to run and status print(chunk._id+","+chunk.shard+","+dataSizeResult.size+","+dataSizeResult.numObjects);
totalSize += dataSizeResult.size;
totalChunks++;
if (dataSizeResult.size == 0) { totalEmpty++ }; //count empty chunks for summary
}
)
print("***********Summary Chunk Information***********");
print("Total Chunks: "+totalChunks);
print("Average Chunk Size (bytes): "+(totalSize/totalChunks));
print("Empty Chunks: "+totalEmpty);
print("Average Chunk Size (non-empty): "+(totalSize/(totalChunks-totalEmpty)));
}
Run Code Online (Sandbox Code Playgroud)
您可以在 a 中的任何分片命名空间上调用它mongos,如下所示:
mongos> AllChunkInfo("test.users");
ChunkID,Shard,ChunkSize,ObjectsInChunk
test.users-_id_MinKey,shard0000,0,0
test.users-_id_10000.0,shard0000,525420,26271
test.users-_id_36271.0,shard0001,1120640,56032
test.users-_id_92303.0,shard0001,153940,7697
***********Summary Chunk Information***********
Total Chunks: 4
Average Chunk Size (bytes): 450000
Empty Chunks: 1
Average Chunk Size (non-empty): 600000
Run Code Online (Sandbox Code Playgroud)
接下来让您再次使用估计找到特定块的信息。这在没有估计的情况下使用更安全,但仍应谨慎使用:
ChunkInfo = function(ns, id){
var configDB = db.getSiblingDB("config");
var db1 = db.getSiblingDB(ns.split(".")[0]);
var key = configDB.collections.findOne({_id:ns}).key;
var chunk = configDB.chunks.find({"_id" : id, }).limit(1).next();
var dataSizeResult = db1.runCommand({datasize:chunk.ns, keyPattern:key, min:chunk.min, max:chunk.max, estimate:true});
print("***********Chunk Information***********");
printjson(chunk);
print("Chunk Size: "+dataSizeResult.size)
print("Objects in chunk: "+dataSizeResult.numObjects)
}
Run Code Online (Sandbox Code Playgroud)
示例输出:
mongos> ChunkInfo("test.users", "test.users-_id_10000.0")
***********Chunk Information***********
{
"_id" : "test.users-_id_10000.0",
"lastmod" : Timestamp(3000, 0),
"lastmodEpoch" : ObjectId("518373f0a962406e4467b054"),
"ns" : "test.users",
"min" : {
"_id" : 10000
},
"max" : {
"_id" : 36271
},
"shard" : "shard0000"
}
Chunk Size: 525420
Objects in chunk: 26271
Run Code Online (Sandbox Code Playgroud)