通过优化索引以消除 MongoDB 中的 "查询目标已超过1000个扫描对象/返回的文档数" 警告

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: MongoDB NoSQL数据库在处理复杂查询时可能出现“查询目标已超过1000个扫描对象/返回的文档数”警告。文章分析了该问题,展示了一个示例集合和相关索引,并提供了查询示例。通过`explain`命令发现查询未有效利用索引。解决方案是遵循ESR规则,创建新索引从而优化查询并消除警告。

概述

MongoDB 是一款功能强大的 NoSQL 数据库,可以灵活扩展以处理大量数据。然而,在处理涉及多个字段和复杂条件的查询时,我们可能会遇到一个警告消息,显示 "查询目标已超过1000个扫描对象/返回的文档数"。在本文中,我们将探讨一个特定的情景,了解此警告的产生原因,并讨论通过优化索引来消除该警告的方法。


问题描述

给定一个MongoDB 集合:

{
    "_id" : ObjectId("abc"),
    "key" : "mykey",
    "val" : "myval",
    "created_at" : ISODate("2023-09-25T07:38:04.985Z"),
    "a_has_sent" : false,
    "b_has_sent" : false,
    "updated_at" : ISODate("2023-09-25T07:38:04.985Z")
}

该集合有两个索引定义如下:

  • 索引1:{"key": {"updated_at": 1}} 名称:"updated_at_1"
  • 索引2:{"key": {"updated_at": 1, "a_has_sent": 1, "b_has_sent": 1}} 名称:"updated_at_1_a_has_sent_1_b_has_sent_1"

"查询目标已超过1000个扫描对象/返回的文档数" (Query Targeting: Scanned Objects / Returned has gone above 1000) 警告是由以下查询触发的:

db.collectionname.find({
    "updated_at": {"$gte": ISODate("2023-09-24")},
    "$or": [
        {"a_has_sent": false},
        {"b_has_sent": false}
    ],
    "key": "key_1"
})

问题研究

我先分析下这个错误 Query Targeting: Scanned Objects / Returned has gone above 1000 产生的原因。依据MongoDB文档 这个警告信息表示你的查询在执行时扫描的文档数量远远超过了实际返回的文档数量,意味着查询的执行效率不高。通常,我们可以通过创建适当的索引来解决这个问题。

针对我们的查询,先查看下 explain 的结果

> db.collectionname.find({ "updated_at": { "$gte": ISODate("2023-09-24")}, "$or": [{ "a_has_sent": false }, {"b_has_sent": false}], "key": "key1"}).explain()

...
          winningPlan: {
            stage: 'FETCH',
            filter: {
              '$and': [
                {
                  '$or': [
                    { a_has_sent: { '$eq': false } },
                    { b_has_sent: { '$eq': false } }
                  ]
                },
                { key: { '$eq': 'key1' } }
              ]
            },
            inputStage: {
              stage: 'IXSCAN',
              keyPattern: { updated_at: 1 },
              indexName: 'updated_at_1',
              isMultiKey: false,
              multiKeyPaths: { updated_at: [] }
            }
          },
          rejectedPlans: [
            {
              stage: 'FETCH',
              filter: {
                '$and': [
                  {
                    '$or': [
                      { a_has_sent: { '$eq': false } },
                      { b_has_sent: { '$eq': false } }
                    ]
                  },
                  { key: { '$eq': 'key1' } }
                ]
              },
              inputStage: {
                stage: 'IXSCAN',
                keyPattern: { updated_at: 1, a_has_sent: 1, b_has_sent: 1 },
                indexName: 'updated_at_1_a_has_sent_1_b_has_sent_1',
...

我们从 winningPlan中观察到使用的索引是 "updated_at_1",而索引 "updated_at_1_a_has_sent_1_b_has_sent_1"是在rejectedPlans,并没有被MongoDB采用,所以导致查询在执行时扫描的文档数量远远超过了实际返回的文档数量。


方案1

我们尝试添加了另一个新索引 {“updated_at”: 1, “key”: 1},希望此查询可以使用新索引来减少扫描的文档数量。不幸的是,我们失败了,这个查询仍然使用了名为"updated_at_1"的索引。


方案2

我们还尝试将find替换为aggregate

> aggregate([{"$match": { "updated_at": { "$gte": ISODate("2023-09-24") }, "$or": [{ "a_has_sent": false }, { "b_has_sent": false}], "key": "key_1"}}])

再次失败,这个查询依然使用了名为"updated_at_1"的索引。


方案3

使用MongoDB The ESR (Equality, Sort, Range) Rule 来优化我们的查询语句

什么是ESR(Equality, Sort, Range)规则

MongoDB的ESR(Equality, Sort, Range)规则是一种用于优化复合索引的方法。复合索引是引用多个字段的索引,可以显著提高查询响应时间。索引键对应于文档字段。在大多数情况下,按照ESR规则来排列索引键有助于创建更高效的复合索引1。

让我们详细了解一下ESR规则的三个方面:

  • Equality(等值):这指的是对单个值的精确匹配。在索引中,首先放置需要精确匹配的字段。索引可以具有多个键,用于处理精确匹配的查询。这些索引键的顺序不影响MongoDB的搜索算法,但为了满足索引的等值匹配,所有精确匹配的索引键必须出现在其他索引字段之前。确保等值测试能够消除至少90%的可能文档匹配,以减少扫描的索引键数量。
  • Sort(排序):排序决定了结果的顺序。排序紧随等值匹配,因为等值匹配减少了需要排序的文档数量。在索引中,只有当查询字段是索引键的子集时,索引才能支持排序操作。如果查询包括了所有前缀键的等值条件,那么对索引键的子集进行排序操作是支持的。例如,如果我们查询汽车制造商为“GM”的文档,并按照型号排序,我们可以创建一个索引:db.cars.createIndex({ manufacturer: 1, model: 1 })。制造商是第一个键,因为它是一个等值匹配。型号按照相同的顺序(1)被索引,以满足查询的需求。
  • Range(范围):范围过滤器用于扫描字段,不需要精确匹配。范围过滤器与索引键的绑定较松。为了提高查询效率,尽量使范围边界尽可能紧凑,并使用等值匹配来限制必须扫描的文档数量。例如,我们可以使用以下查询来查找价格大于等于15000的汽车:`db.cars.find({ price: { $gte: 15000 } })``

遵循ESR规则,我们重新创建了索引{"key": 1, "a_has_sent": 1, "b_has_sent": 1, "updated_at": 1},现在可以使用此索引来执行查询。这个警告"查询目标已超过1000个扫描对象/返回的文档数" (Query Targeting: Scanned Objects / Returned has gone above 1000) 也消除了。

          winningPlan: {
            stage: 'FETCH',
            filter: {
              '$or': [
                { a_has_sent: { '$eq': false } },
                { b_has_sent: { '$eq': false } }
              ]
            },
            inputStage: {
              stage: 'IXSCAN',
              keyPattern: {
                key: 1,
                a_has_sent: 1,
                b_has_sent: 1,
                updated_at: 1
              },
              indexName: 'key_1_a_has_sent_1_b_has_sent_1_updated_at_1',
              isMultiKey: false,
              isUnique: false,
              isSparse: false,
              isPartial: false,
              indexVersion: 2,

总结

  • 通过应用ESR规则,提高了查询效率,并消除了"查询目标已超过1000个扫描对象/返回的文档数"警告。
  • 对于查询语句,需要检查explain结果,查看是否使用了最佳的索引,使查询效率最高。
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
28天前
|
NoSQL MongoDB 数据库
MongoDB 更新文档
10月更文挑战第14天
40 2
|
1月前
|
存储 NoSQL MongoDB
掌握MongoDB索引优化策略:提升查询效率的关键
在数据库性能调优中,索引是提升查询效率的利器。本文将带你深入了解MongoDB索引的内部工作原理,探讨索引对查询性能的影响,并通过实际案例指导如何针对不同的查询模式建立有效的索引。不仅将涵盖单一字段索引,还会探讨复合索引的使用,以及如何通过分析查询模式和执行计划来优化索引,最终实现查询性能的最大化。
|
21天前
|
存储 NoSQL MongoDB
MongoDB 查询分析
10月更文挑战第21天
11 1
|
21天前
|
NoSQL MongoDB 索引
MongoDB 覆盖索引查询
10月更文挑战第21天
23 1
|
27天前
|
SQL NoSQL MongoDB
MongoDB 查询文档
10月更文挑战第15天
14 1
|
27天前
|
NoSQL MongoDB
MongoDB 删除文档
10月更文挑战第15天
41 0
|
28天前
|
存储 JSON NoSQL
MongoDB 插入文档
10月更文挑战第14天
29 0
|
29天前
|
人工智能 NoSQL 机器人
MongoDB Atlas与YoMio.AI近乎完美适配:推理更快速、查询更灵活、场景更丰富
随着MongoDB的新发布和革新,YoMio.AI的“闪电式发展”值得期待。
|
29天前
|
存储 监控 NoSQL
TDengine 3.3.3.0 版本上线:优化监控、增强 MongoDB 支持
今天我们非常高兴地宣布,TDengine 3.3.3.0 版本正式发布。本次更新引入了多项重要功能和性能优化,旨在为用户提供更高效、更灵活的数据解决方案。
44 0
|
20天前
|
NoSQL Cloud Native atlas
探索云原生数据库:MongoDB Atlas 的实践与思考
【10月更文挑战第21天】本文探讨了MongoDB Atlas的核心特性、实践应用及对云原生数据库未来的思考。MongoDB Atlas作为MongoDB的云原生版本,提供全球分布式、完全托管、弹性伸缩和安全合规等优势,支持快速部署、数据全球化、自动化运维和灵活定价。文章还讨论了云原生数据库的未来趋势,如架构灵活性、智能化运维和混合云支持,并分享了实施MongoDB Atlas的最佳实践。