默认范围分片键 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的主要内容,如果未能解决你的问题,请参考以下文章