Mongodb分片集合限制,导致一次上线紧急回滚

bbef70d269bb3f4a82a6be0142236abf.jpeg

背景

    MongoDB 集群分片中某个节点的磁盘使用率已经达到了 75%,发现一个名为 "visitor" 的集合数据量已经达到了 20 多亿条,占用了 260GB 的磁盘空间。我与研发团队讨论后决定清理数据,需要保留最近半年的数据,但是时间字段没有索引。因此采用的方案是新建新的集合、createTime字段添加索引、同时开启集合分片,使用阿里云DTS实时将数据同步到新的集合,然后研发修改代码将调用老集合的地方更改成新集合。上线后服务报错,紧急回滚了。

问题分析

    根据告警信息提示“Query for sharded findAndModify must contain the shard key”找不到分片键,按照常理如果查询、更新、删除等操作不带分片键,无非就是性能慢,耗时久,不会报错的。立即找研发要到了代码调取数据库的语句,发现这个语句是查询并更新操作,查阅mongodb官方文档,分片集合有一些操作限制。

问题报错:

d206600e8673a42c1f7cb8bcc0756d45.png

问题语句:

db.getCollection("el_frequent_visitor_tmp0425").findOneAndUpdate(    
{ "_id": ObjectId("62982c8c5634657e96467e46") },     
{ $set: { "status": "modified" } },     
{ returnDocument: "after" } )

注意事项:

分片环境中的操作不可用

  • $where不允许引用对象 从$where函数。

  • 分片中不支持 geoSearch 命令 环境。

  • 在 MongoDB 4.2 及更早版本中,您不能在 $lookup 阶段的参数中指定分片集合

分片集合中的单个文档修改操作

在MongoDB中,对分片集合进行单个文档修改操作时有一些限制。特别是针对使用了分片键的操作,需要注意以下几点:

  • updateOne()、updateMany()和deleteOne()操作:如果在对分片集合进行这些操作时指定了 upsert: true 或 multi: false 选项,那么操作的查询条件必须包含分片键或查询字段。如果操作中没有包含分片键或查询字段,则会返回错误。这是因为在分片环境下,MongoDB需要确保操作的一致性和可靠性,因此要求操作条件中包含分片键以确保数据的正确性。

  • findOneAndUpdate()操作:如果要对分片集合使用findOneAndUpdate()操作,查询过滤条件必须包含分片键的等值条件,以便比较键和值。换句话说,查询条件中必须包含一个等于分片键的条件,以便MongoDB能够确定要在哪个分片上执行操作。这样可以确保操作的一致性,并避免在分片集合上出现错误或不一致的结果。

分片集合中的唯一索引

1、在MongoDB中,唯一索引在分片集合中有一些限制。通常情况下,MongoDB不支持跨分片的唯一索引,除非唯一索引包含分片键的完整前缀。在这种情况下,MongoDB会在整个键上强制执行唯一性约束,而不是在单个字段上。

 举个例子:

假设我们有一个分片集合,其分片键是{shardKey: 1, otherField: 1}。如果我们在这个集合上创建了一个唯一索引,如{shardKey: 1},MongoDB会在整个{shardKey, otherField}键上强制执行唯一性约束,而不是仅在shardKey字段上。这意味着即使在不同分片上,只要整个键的值都是唯一的,就不会出现重复的值。

2、这个限制是因为MongoDB在分片环境下需要确保数据的一致性和可靠性。如果允许跨分片的唯一索引,那么在不同分片上的数据变更可能会导致数据不一致的情况发生,因为MongoDB无法轻易地在全局范围内验证唯一性。因此,MongoDB要求唯一索引至少包含分片键的完整前缀,以确保分片键的唯一性。

分片现有集合数据大小

在MongoDB中,对已有的集合进行分片操作时,存在一些限制,其中之一是集合的大小不能超过特定的限制。这些限制可以根据所有分片键值的平均大小和配置的分块大小来估算。

使用以下公式计算理论最大值 集合大小:

maxSplits = 16777216 (bytes) / <average size of shard key values in bytes>

maxCollectionSize (MB) = maxSplits * (chunkSize / 2)

  1. 平均分片键值大小(Average Shard Key Size):首先,需要计算所有分片键值的平均大小。这可以通过对所有文档的分片键值进行求和,然后除以文档总数来获得平均值。

  2. 配置的分块大小(Configured Chunk Size):分片集合中数据的划分是以分块为单位进行的。配置的分块大小是在启用分片时定义的。根据这个配置值,MongoDB会将集合数据划分为不同的分块。

在进行初始分片操作时,如果计算结果显示集合的大小接近或略小于目标集合的大小,为了确保成功的初始分片,可以考虑增加分块大小。这是因为在初始分片过程中,分块的大小直接影响了数据的划分和分布,而适当增加分块大小可以确保数据能够充分分散到不同的分片中,从而避免出现过度拥挤或不均匀的情况。

如果对计算结果是否太接近目标集合大小存在疑问,通常更好的做法是增加分块大小,以确保初始分片的成功。这样可以预防潜在的问题,如数据分布不均匀或分片过度拥挤,从而减少后续调整的必要性。

在成功进行了初始分片之后,可以根据实际情况适当减少分块大小。如果稍后需要减少分块大小,可能需要一段时间才能使所有分块都分裂到新的大小。在修改分块大小时,需要注意确保数据的完整性和可用性,以及对数据库性能可能产生的影响。可以参考《在分片集群中修改分块大小》的指南来进行分块大小的修改操作。

69e5b887a86d8710ecefd10b61272ebd.png

注意:

这些限制只适用于初始的分片操作。一旦成功启用了分片,分片集合可以随着数据的增长而扩展到任意大小。

总结:
    在分片集合中进行查询、更改、删除操作时,最好带上分片键,这样可以提高效率并快速定位数据所在的分片。上线时遇到的回滚问题是因为执行了findOneAndUpdate()操作,但该操作没有带上分片键条件,导致出错。为了解决这个问题,一种较好的方法是在该语句中添加分片键条件。然而,并不是每条语句都能带上分片键条件,因此针对这种情况,可以将查询和更新拆分成两条语句。虽然这样做会降低性能,但能确保操作的正确性。

加入数据库技术交流群:

0891b7b023bc6b0d4e34d2b7b5b511ef.png

进群福利:

1、知识共享与学习机会

2、问题求助与解决支持

3、技术沉淀和分享平台

4、及时了解行业动态

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值