【问题标题】:Not able to create covered query in MongoDB无法在 MongoDB 中创建覆盖查询
【发布时间】:2017-07-01 19:44:25
【问题描述】:

我在创建覆盖查询时遇到问题。我正在使用 Mongo 3 最新版本。这是我在 MongoDB 中插入了 10006 个文档的示例数据。

db.order.insert({ _id: 1, cust_id: "abc1", ord_date: ISODate("2012-11-02T17:04:11.102Z"), status: "A", amount: 50 })
db.order.insert({ _id: 2, cust_id: "xyz1", ord_date: ISODate("2013-10-01T17:04:11.102Z"), status: "A", amount: 100 })
db.order.insert({ _id: 3, cust_id: "xyz1", ord_date: ISODate("2013-10-12T17:04:11.102Z"), status: "D", amount: 25 })
db.order.insert({ _id: 4, cust_id: "xyz1", ord_date: ISODate("2013-10-11T17:04:11.102Z"), status: "D", amount: 125 })
db.order.insert({ _id: 5, cust_id: "abc1", ord_date: ISODate("2013-11-12T17:04:11.102Z"), status: "A", amount: 25 })

对于涵盖的查询,查询中的所有字段都是索引的一部分,因此我为状态、ord_date、cust_id 和金额字段创建了索引,例如:

db.orders.createIndex({status: 1})
db.orders.createIndex({amount: 1})
db.orders.createIndex({ord_date: 1})
db.orders.createIndex({cust_id: 1})

我已经执行了以下查询。

          db.orders.find(
                 {status : "A"},{ord_date : 1, cust_id : 1}
          ).sort({ amount: -1 }).explain()

但是这个解释查询返回 executionStats.totalDocsExamined = 200 而不是 executionStats.totalDocsExamined = 0。这意味着当我执行查询时它是扫描文档。在 Mongo 3 中,我们可以使用 executionStats.totalDocsExamined 而不是 indexOnly 来检查索引覆盖的查询。

谁能建议我在涵盖的查询中做错了什么?

这是我在 Markus 提出索引建议后的输出:

{
"queryPlanner" : {
    "plannerVersion" : 1,
    "namespace" : "local.orders",
    "indexFilterSet" : false,
    "parsedQuery" : {
        "status" : {
            "$eq" : "A"
        }
    },
    "winningPlan" : {
        "stage" : "PROJECTION",
        "transformBy" : {
            "_id" : 1,
            "ord_date" : 1,
            "cust_id" : 1
        },
        "inputStage" : {
            "stage" : "SORT",
            "sortPattern" : {
                "amount" : -1
            },
            "inputStage" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "status" : {
                        "$eq" : "A"
                    }
                },
                "direction" : "forward"
            }
        }
    },
    "rejectedPlans" : [ ]
},
"executionStats" : {
    "executionSuccess" : true,
    "nReturned" : 10004,
    "executionTimeMillis" : 70,
    "totalKeysExamined" : 0,
    "totalDocsExamined" : 10018,
    "executionStages" : {
        "stage" : "PROJECTION",
        "nReturned" : 10004,
        "executionTimeMillisEstimate" : 70,
        "works" : 20026,
        "advanced" : 10004,
        "needTime" : 10021,
        "needFetch" : 0,
        "saveState" : 157,
        "restoreState" : 157,
        "isEOF" : 1,
        "invalidates" : 0,
        "transformBy" : {
            "_id" : 1,
            "ord_date" : 1,
            "cust_id" : 1
        },
        "inputStage" : {
            "stage" : "SORT",
            "nReturned" : 10004,
            "executionTimeMillisEstimate" : 70,
            "works" : 20026,
            "advanced" : 10004,
            "needTime" : 10020,
            "needFetch" : 0,
            "saveState" : 157,
            "restoreState" : 157,
            "isEOF" : 1,
            "invalidates" : 0,
            "sortPattern" : {
                "amount" : -1
            },
            "memUsage" : 960384,
            "memLimit" : 33554432,
            "inputStage" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "status" : {
                        "$eq" : "A"
                    }
                },
                "nReturned" : 10004,
                "executionTimeMillisEstimate" : 10,
                "works" : 10020,
                "advanced" : 10004,
                "needTime" : 15,
                "needFetch" : 0,
                "saveState" : 157,
                "restoreState" : 157,
                "isEOF" : 1,
                "invalidates" : 0,
                "direction" : "forward",
                "docsExamined" : 10018
            }
        }
    },
    "allPlansExecution" : [ ]
},
"serverInfo" : {
    "host" : "pcd32",
    "port" : 27017,
    "version" : "3.0.7",
    "gitVersion" : "6ce7cbe8c6b899552dadd907604559806aa2esd5"
}

}

【问题讨论】:

  • 请显示.explain()的完整输出
  • 我已经更新了我的问题中的输出。

标签: mongodb mongodb-query mongo-java nosql


【解决方案1】:

虽然有index intersections in MongoDB,但使用起来可能非常棘手。但是,坚持经验法则是一个相当安全的选择:

在创建查询 MongoDB 时,假设一次只能使用一个索引

covered queries 尤其如此,详见文档:

当满足以下两个条件时,索引覆盖查询:

  • 查询中的所有字段都是索引的一部分,并且

  • 结果中返回的所有字段都在同一个索引中。

拥有compound index 并没有缺点,如果精心设计的话,因为只使用该索引的一部分的查询也可以使用它。

因此,为了覆盖您的查询,您需要在索引中包含要返回的所有键。由于您没有 limit the fields returned(MongoDB 术语中的“投影”),我假设您还需要返回 _id 字段。此外,您的索引应该反映您的排序顺序。所以你的索引应该是这样的:

db.orders.createIndex({_id:1,status:1, ord_date:1,cust_id:1,amount:-1})

供您查询。顺序很重要,因此为了充分利用新创建的索引,其他查询应遵循相同的字段顺序。

【讨论】:

  • 我根据我的返回字段只创建​​了“db.orders.createIndex({_id:1,status:1, ord_date:1,cust_id:1,amount:-1})”索引。但仍然结果似乎 executionStats.totalDocsExamined = 200 而不是 0。我是否需要创建除您的索引之外的任何其他索引?
  • @sus007 请在为您的问题创建索引后添加“yourQuery.explain()”的输出。
  • 我已经创建了这样的索引:db.orders.createIndex({status: 1, amount: 1, ord_date: 1, cust_id: 1});我解决了这个问题。所以索引中字段的顺序对于覆盖查询非常重要。
  • Markus,我已尝试使用您建议的索引,但它不会在解释方法中为 totalDocsExamine 返回 0 值。如果我错了,请纠正我。
  • 请编辑您的帖子并添加您的查询输出“.explain()”
【解决方案2】:

如果您还需要_id 字段,那么下面的复合索引应该会为您提供一个覆盖查询:

 db.order.createIndex({status:1, amount:-1, ord_date:1, cust_id :1, _id:1})

如果您不需要_id 字段,则在find() 中使用_id : 0,这样_id 就不会被检索到,您也可以将其从索引中删除。 请注意,在覆盖查询中,与正在执行的实际查询相比,字段的排序对于在查询执行中使用的索引很重要。

【讨论】:

猜你喜欢
  • 2014-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2021-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多