默认范围分片键 mongodb

Posted

技术标签:

【中文标题】默认范围分片键 mongodb【英文标题】:Default range shard key mongodb 【发布时间】:2014-11-06 03:53:29 【问题描述】:

我有一个带有 2 个分片(比如说 A 和 B)的 mongodb 分片,每个分片有 17GB 可用空间。我将包含对象 ID 的 _id 设置为分片键。

以下是用于设置 db 和 collection 的命令。

sh.enableSharding("testShard");
sh.shardCollection("testShard.shardedCollection", _id:1);

然后我尝试向 mongos 服务器发起 4,000,000 个插入查询。我执行脚本低于 4 次。

for(var i=0; i<1000000; i++)
  db.shardedCollection.insert(x:i);

使用 _id 作为分片键,据我了解,上述 4000000 个文档将适合 1 个分片,所有插入将仅发生在 A 分片中。

然而,结果并不像我预期的那样,在 A 分片中插入了约 130 万份文档,在 B 分片中插入了另外约 270 万份文档。

为什么会这样? shard coll 设置命令中是否缺少某些内容?还是我的理解有误,可能mongodb中有default range shard key之类的东西?

如果有人可以分享默认范围分片键的行为(不知道标签),那将非常有帮助。

下面是 sh.status() 结果

  shard key:  "_id" : 1 
  chunks:
    B  5
    A  5
   "_id" :  "$minKey" : 1   -->>  "_id" : ObjectId("540c703398c7efdea6037cbc")  on : B Timestamp(6, 0) 
   "_id" : ObjectId("540c703398c7efdea6037cbc")  -->>  "_id" : ObjectId("540c703498c7efdea603bfe3")  on : A Timestamp(6, 1) 
   "_id" : ObjectId("540c703498c7efdea603bfe3")  -->>  "_id" : ObjectId("540c704398c7efdea605d818")  on : A Timestamp(3, 0) 
   "_id" : ObjectId("540c704398c7efdea605d818")  -->>  "_id" : ObjectId("540c705298c7efdea607f04e")  on : A Timestamp(4, 0) 
   "_id" : ObjectId("540c705298c7efdea607f04e")  -->>  "_id" : ObjectId("540c707098c7efdea60c20ba")  on : B Timestamp(5, 1) 
   "_id" : ObjectId("540c707098c7efdea60c20ba")  -->>  "_id" : ObjectId("540c7144319c0dbee096f7d6")  on : B Timestamp(2, 4) 
   "_id" : ObjectId("540c7144319c0dbee096f7d6")  -->>  "_id" : ObjectId("540c7183319c0dbee09f58ad")  on : B Timestamp(2, 6) 
   "_id" : ObjectId("540c7183319c0dbee09f58ad")  -->>  "_id" : ObjectId("540eb15ddace5b39fbc32239")  on : B Timestamp(4, 2) 
   "_id" : ObjectId("540eb15ddace5b39fbc32239")  -->>  "_id" : ObjectId("540eb192dace5b39fbca8a84")  on : A Timestamp(5, 2) 
   "_id" : ObjectId("540eb192dace5b39fbca8a84")  -->>  "_id" :  "$maxKey" : 1   on : A Timestamp(5, 3) 

【问题讨论】:

【参考方案1】:

正如@LalitAgarwal 已经指出的那样,ObjectIds 默认情况下会生成错误的分片键。但是,如果您并不真正关心您的数据位于哪个分片,而只想让写入操作和块在您的分片之间均匀分布,那么这很容易获得:

db.shardedCollection.ensureIndex(_id:"hashed");
sh.enableSharding("testShard");
sh.shardCollection("testShard.shardedCollection", _id:"hashed");

但是,这会带来一些(通常可以忽略不计)缺点:

    您有一个附加索引仅用于分片,没有其他用例 此索引会占用一些 RAM,这是高负载生产节点上的宝贵资源 此人工索引在插入期间需要写入操作

更好的方法是找到非人工分片键。详情请阅读Considerations for Selecting Shard Keys。简而言之:

    找到一个字段或字段组合,可以明确标识每个文档(组合)彼此之间存在很大差异。理想情况下,这些都应该是您查询的字段。 将此字段或字段组合用作您的_id。由于 _id 字段无论如何都需要索引,并且您查询这些字段,因此您摆脱了不需要的索引。 使用选定的 _id 字段作为您的分片键。

【讨论】:

我会按objectId顺序读取文档。因此,我认为牺牲 1 个分片的写入是个好主意,但是当我想按顺序读取时,它也会被本地化为 1 个分片。 不,它不会,因为平衡器迟早会启动。 ;)【参考方案2】:

是的,你是对的,它应该进入单个分片。但是,虽然在单个分片上进行插入,但平衡器也会平衡分片并将块移动到其他分片。

话虽如此,您应该做的是通过从您的 mongos 调用以下命令来停止/禁用平衡器。

http://docs.mongodb.org/manual/reference/method/sh.disableBalancing/#sh.disableBalancing

sh.disableBalancing(namespace)
//namespace     string  The namespace of the collection.

完成后,开始插入并查看所有插入的去向。

对于 _id 字段分片,你也可以看这里:

http://docs.mongodb.org/manual/faq/sharding/#can-you-shard-on-the-id-field

Be aware that ObjectId() values, which are the default value of the _id field, 
increment as a timestamp. As a result, when used as a shard key, all new documents
inserted into the collection will initially belong to the same chunk on a single 
shard. Although the system will eventually divide this chunk and migrate its contents 
to distribute data more evenly, at any moment the cluster can only direct insert 
operations at a single shard. This can limit the throughput of inserts. If most of 
your write operations are updates, this limitation should not impact your performance. 
However, if you have a high insert volume, this may be a limitation.

【讨论】:

当 balancer 被禁用时,如果第一个 shard 已满,是否会继续向下一个 shard 写入文档? 我认为没有。对于 mongos 写入任何特定分片,该特定块范围需要存在于该分片中。禁用平衡器后,您将不会有任何块到下一个分片。为了使它成为可能,您必须手动拆分块并将其移动到另一个分片,然后 mongos 将开始将数据放入另一个分片。

以上是关于默认范围分片键 mongodb的主要内容,如果未能解决你的问题,请参考以下文章

MogoDB 分片键

NoSQL生态系统——hash分片和范围分片两种分片

走进MongoDB---- 分片

mongoDB分片集群

MongoDBmongodb设置用户访问权限

mycat分片规则