【MongoDB】Shard key

本文介绍了MongoDB中的分片功能,包括使用Shell命令进行分区的方法、两种常见的分片策略(Hashed和Ranged),以及选择分片键时的注意事项。通过具体示例,帮助读者更好地理解和应用MongoDB分片。

1. 使用Shell命令分区

MongoDB提供的分区功能,本节简单介绍在mongo shell的分区命令。

  • sh.enableSharding(database) 指定database
  • sh.shardCollection(namespace, key, unique, options) 指定Collection
    这里写图片描述

【注意】如果库中还没有指定的database则需要顺序执行上面两个命令,如果已经存在指定database,应该可以直接执行命令2。

还有一种命令格式是db.runCommand(),具体的参加官方文档:
https://docs.mongodb.com/manual/reference/command/enableSharding/#dbcmd.enableSharding
https://docs.mongodb.com/manual/reference/command/shardCollection/

2. Sharding Strategy

有两种常用的Sharding方式:hashed、ranged。

2.1 Hashed Sharding

Hashed Sharding方式使用在某一字段(Field)上指定哈希索引的方式指定其为shard key。
指定shard key例子:sh.shardCollection(“ccse.stu”, {“name”:”hashed”})
这里写图片描述

MongoDB自动计算哈希值,用户不需要对shard key进行哈希。
【注】不清楚MongoDB自带的哈希函数是什么。

哈希分片的好处是使得数据分区更加均匀,但是不支持范围查询,做范围查询就是在所有片上进行搜索。

2.2 Ranged Sharding

基于范围的分区的好处是能够支持范围查询,指定shard key:sh.shardCollection(“ccse.stu”, {“name” : 1, “height” : 1})。

范围分区可以指定多个字段,这和指定复合索引差不多。但是需要注意的是:
1. Hashed Sharding只能指定一个字段
2. Ranged Sharding不能同时指定hash字段,即像这样:sh.shardCollection("ccse.stu", {"name" : 1, "height" : "hashed"}),是不允许的。

下面Ranged Sharding下选取shard key的一些官方建议:

2.2.1 Shard Key的基数

Shard Key的基数决定了Chunk最多有多少。因为同一Shard Key的被存储在一个Chunk中,Chunk分裂不会将同一Shard Key的拆分。

假如有如下模型的Document:
{
price : 20
month : 1
}

如果指定month为shard key,那么最多只要12个chunk,如果数据一多,那么只能是chunk不断增长,就算增加分片也没有意义。

如何想要指定的shard key的基数不大,可以考虑复合索引,再加一个基数大的字段。

2.2.2 Shard Key的频率

再以上面那个例子说明,如果6月的数据特别多,而其他月份的数据很少,这样就是频率问题了,也是需要注意的。

2.2.3 Shard Key非单调变化

假如2.2.1节中的例子选择price为shard key,如果shard key单调递增,那么数据虽然会被分布到不同节点,但在插入时有可能只会往一个节点上插入。因为shard key相近的分布在同一节点,这应该很好理解。
这里写图片描述

2.2.4 Unique Shard Key

对于一个分区的Collection,只有可能存在以下两种unique key:
1. _id
2. 在shard key上的建立的索引,或复合索引里shard key包含在首位。

3. Shard Key 的限制

Shard Key size

一个 shard key 大小不能超过512个字节

Shard Key是不可变的

如果你想要换另一种shad key,官方建议以下步骤:

  • Dump all data from MongoDB into an external format.
  • Drop the original sharded collection.
  • Configure sharding using the new shard key.
  • Pre-split the shard key range to ensure initial even distribution.
  • Restore the dumped data into MongoDB.
    简言之,就是先备份数据,然后删除,重新选择shard key,再导入数据。

Shard Key在文档中的值是不变的

  • 不能换Shard Key
  • 不能更新Shard Key的值

4. 一些实际经验总结

  • 插入时Shard Key不能重复,否则报错_dup_key
  • 更新时update文档必须指定Shard Key,如下图测试
    这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

参考

https://docs.mongodb.com/manual/core/sharding-shard-key/#shard-key-indexes
https://docs.mongodb.com/manual/core/sharded-cluster-requirements/
https://docs.mongodb.com/manual/reference/limits/#limits-shard-keys

<think>好的,我现在需要解决MongoDB分片集群中无法提取精确分片键的问题。首先,我得理解分片键是什么以及它的作用。分片键是用于将数据分布到不同分片上的字段或字段组合,确保查询能正确路由到对应的分片。如果出现无法提取精确分片键的情况,可能意味着查询中没有包含分片键,或者分片键的值不明确。 接下来,用户提到的错误信息是“could not extract exact shard key”。根据MongoDB的文档,当执行更新或删除操作时,如果无法确定分片键的精确值,就会报这个错误。这可能是因为查询条件中没有包含分片键的所有字段,或者使用了某些操作符导致分片键无法被准确识别。例如,使用$exists或者正则表达式可能会让MongoDB无法确定具体的分片键值,从而无法定位到正确的分片。 然后,我需要考虑可能的解决方案。首先,确保所有更新或删除操作都包含完整的分片键作为查询条件的一部分。如果分片键是复合键,那么必须包含所有字段。其次,避免在分片键字段上使用无法精确匹配的操作符,比如$in、$or或者正则表达式。另外,检查应用程序的逻辑,确保在操作时能获取到完整的分片键信息。如果确实无法在查询中提供分片键,可能需要考虑调整分片策略,比如使用哈希分片或者范围分片,但这可能会影响查询性能。 此外,用户可能需要检查是否存在分片键数据不一致的情况,例如分片键字段被修改或缺失,这也会导致无法提取精确值。在这种情况下,需要修复数据的一致性,确保分片键字段在文档中存在且未被不当修改。 最后,参考MongoDB的官方文档,建议在设计分片键时选择高基数、低频率变化的字段,并且尽可能让查询模式与分片键匹配,这样可以减少此类问题的发生。如果问题持续,可能需要详细分析具体的查询语句和数据模型,以找到根本原因。</think>### 分片键无法精确提取的解决方案 在MongoDB分片集群中,分片键(Shard Key)是数据分布的核心依据。当执行更新或删除操作时,若查询条件无法精确匹配分片键,会触发错误`could not extract exact shard key`。以下是逐步解决方案: #### 1. **确保查询包含完整分片键** - **问题根源**:更新或删除操作的查询条件未包含分片键的全部字段(尤其是复合分片键)[^1]。 - **解决方案**: - 检查操作语句,例如: ```javascript // 错误示例:复合分片键为 {user_id:1, order_id:1} db.orders.deleteOne({user_id: 100}); // 缺少order_id字段 ``` - 修改为包含所有分片键字段: ```javascript db.orders.deleteOne({user_id: 100, order_id: 200}); // 正确 ``` #### 2. **避免非精确匹配操作符** - **问题根源**:在分片键字段上使用`$exists`、`$regex`或范围查询(如`$gt`)会导致无法定位分片。 - **解决方案**: - 使用精确匹配值: ```javascript // 错误示例:使用范围查询 db.orders.updateOne({user_id: {$gt: 100}}, {$set: {status: "expired"}}); ``` - 改为精确值或批量操作: ```javascript db.orders.updateOne({user_id: 100, order_id: 200}, {$set: {status: "expired"}}); ``` #### 3. **检查分片键数据一致性** - **问题根源**:分片键字段缺失或被修改(如通过`$unset`或`$rename`)。 - **解决方案**: - 修复数据不一致: ```javascript // 添加缺失的分片键字段 db.orders.updateMany({user_id: {$exists: false}}, {$set: {user_id: "default"}}); ``` #### 4. **调整分片策略(长期优化)** - **适用场景**:分片键设计不合理(如低基数或频繁变化)。 - **解决方案**: - 改用哈希分片键分散数据: ```javascript sh.shardCollection("db.orders", {user_id: "hashed"}); ``` - 参考MongoDB分片键设计最佳实践。 --- ### 相关问题 1. 如何选择合适的分片键以提升查询性能? 2. 分片集群中如何处理分片键字段的修改? 3. MongoDB分片集群的数据均衡机制是什么? : Hadoop Beginners Guide. *Get the best out of Cassandra using this efficient recipe bank*.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值