具有复杂查询的MongoDB索引覆盖率



Mongo DB 3.4.6版

我有一个文档结构类似于以下的集合:

{
organization: "ABC123",
tags: ["MARTHA WASHINGTON", "+15552082000"],
updatedAt : ISODate("2020-10-09T17:19:44.861Z"),
createdAt : ISODate("2020-01-14T19:46:15.957Z"),
}

我需要能够按组织进行查询;以";在标签数组上,并可选择按updatedAt或createdAt排序。为了实现这一点,我创建了以下索引:

{
"organization" : 1,
"tags" : 1,
"createdAt" : -1
}

这是一个多键复合索引,基于我对Mongo的理解,它应该允许我在所有情况下覆盖查询。如果我执行这样的查询:

db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/})

查询由索引覆盖——我看到一个FETCH/IXSCAN阶段。

同样,如果我删除regex查询并添加排序,那么查询就完全覆盖了。

db.getCollection('data').find({"organization": "ABC123", "search": "MARTHA WASHINGTON"}).sort({"createdAt":-1})

但是,如果我组合正则表达式和排序选项,我会突然在查询中看到一个额外的sort阶段。示例查询:

db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/}).sort({"createdAt":-1})

以下是解释的获胜计划输出:

"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"createdAt" : -1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"organization" : 1,
"tags" : 1,
"createdAt" : -1
},
"indexName" : "tag matches by organization",
"isMultiKey" : true,
"multiKeyPaths" : {
"organization" : [],
"search" : [ 
"search"
],
"createdAt" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"organization" : [ 
"["ABC123", "ABC123"]"
],
"tags" : [ 
"["MARTHA WASHINGTON", "MARTHA WASHINGTOO")", 
"[/^MARTHA WASHINGTON/, /^MARTHA WASHINGTON/]"
],
"createdAt" : [ 
"[MaxKey, MinKey]"
]
}
}
}
}
},

我很困惑为什么索引中没有包含这种查询组合。我的理解是,一开始的额外排序阶段会导致大型集合的性能缓慢。有人能提供一些指导吗?我错过了什么限制吗?

更新:删除正则表达式查询时的获胜计划

"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"organization" : 1,
"search" : 1,
"createdAt" : -1
},
"indexName" : "tag matches by organization",
"isMultiKey" : true,
"multiKeyPaths" : {
"organization" : [],
"search" : [ 
"search"
],
"createdAt" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"organization" : [ 
"["ABC123", "ABC123"]"
],
"tags" : [ 
"["MARTHA WASHINGTON", "MARTHA WASHINGTON"]"
],
"createdAt" : [ 
"[MaxKey, MinKey]"
]
}
}
},

另一个答案不太准确。从文档

对于区分大小写的正则表达式查询,如果字段存在索引,则MongoDB将正则表达式与索引中的值进行匹配,这可能比集合扫描更快。

Mongo能够利用带有正则表达式的索引,显然,如果您的正则表达式是后缀正则表达式,那么集合扫描实际上可能会更快,因为Mongo必须读取整个索引树才能满足它。

那么,您的查询中发生了什么?为什么获胜计划是sort?虽然这可能是获取结果的最佳方式,但Mongo也有可能只是选择了错误的计划。

首先让我们了解一下Mongo是如何选择获胜计划的,计划评估是基于对给定查询的候选计划进行比较,看看哪一个返回的第一批结果(默认为101个文档(具有最少的总体";工作";。works分数是查询阶段(索引键比较、获取文档等(所涉及的不同工作的代理。如果多个计划在评估期间执行相同的工作,那么有一些小的平局奖金可以帮助选择要缓存的计划。基本上Mongo执行一个小的";种族;等待谁获胜。

因此,在您的情况下,由于带有索引的regex特性,sort阶段获胜,如果您完全运行计划而不是小样本,则可能会选择不同的计划。

我建议您使用提示进行自己的测试,这会强制Mongo使用某个索引,这意味着您可以强制Mongo为您的查询制定获胜计划。我个人认为(显然具体取决于正则表达式(,您可以通过这样做来提高性能,因为排序第一几乎不是所有的"正则表达式";最好的";计划

假设集合中有两个字段:name和age,并按名称进行筛选,按年龄排序。

假设您有以下文档:

JON 30
JON 45
JONATHAN 40

假设您在(name,age(上创建了一个索引。此索引按上面列出的顺序排列文档。

如果查询name = JON并按年龄排序,则所有条件都与索引完全匹配,并且只能通过索引遍历获得(JON, 30), (JON, 45)的输出。

如果查询name =~ ^JON并按年龄排序,那么您期望的输出现在是(JON, 30), (JONATHAN, 40), (JON, 45)。这种排序在索引中不存在,因为名称匹配现在不准确,因此服务器必须对结果集进行排序才能提供它

最新更新