仅在查询第二个字段时查询复合索引上的优化程序索引选择

时间:2013-06-17 16:32:10

标签: mongodb mongodb-query mongodb-indexes

假设我有compound index { a: 1, b: 1 }

查询db.Collection.find( { b: 1 } )不使用此索引。 query optimizer似乎没有选择此索引作为候选运行。

但是,如果您专门hint索引,则查询运行得更快,而nscan要低得多:

db.Collection.find( { b: 1 } ).hint( { a: 1, b: 1 } )

我的问题是,如果使用索引导致更快的查询, 为什么query optimizer会单独b忽略我的查询中的索引? < /强>

3 个答案:

答案 0 :(得分:2)

从链接到“复合索引”的页面:“复合索引支持对索引中任何字段前缀的查询。”索引有助于不是前缀的查询的情况是相当具体的,并且与a的值的分布有关(我相信它可以作为{的可能值的数量做得更好) {1}}减少)。在这种情况下,最好的办法是不要尝试使用索引,因为这可能会使事情变慢。

在评论中,你建议在最坏的情况下它应该不会慢很多,但可以给出很大的改进。好吧,我们来试试吧。我构建了一个包含10 ^ 6个文档的集合,其中每个文档a都是i。在我的假设中,这是使用索引{a: i, b: i+1}时仅b查询的最坏情况。

查询

{a: 1, b: 1}

我们发现它在大约350毫秒内扫描了1,000,000份文件(并不奇怪)。对于未编入索引的查询,这不错。现在,让我们提示索引:

db.testing.find({b: 0}).explain()

这次它只扫描了954,546份文件。我不太了解MongoDB索引来解释这一点。但是,这个略小的扫描大约需要2300毫秒,或者是无索引查询的6.5倍。

所以,是的,索引不佳的查询可能 比未编入索引的查询差。但这并没有完全回答你的问题 - 为什么查询优化器不能解决这个问题呢?

查询优化器在第一次查看查询时并行运行不同的计划,并记住最适合将来的查询(这有时会重新评估)。但是,它只会尝试候选索引 - 也就是那些索引的某些非空前缀与查询的某些部分匹配的索引。当然,按此标准,db.testing.find({b: 0}).hint("a_1_b_1").explain() 不是仅{a: 1, b: 1}的查询的候选索引。

我建议在b上创建第二个索引(或者至少使用该前缀),或者反转已有的第二个索引(创建{b: 1}然后删除旧的索引)

答案 1 :(得分:0)

通常使用复合索引,用于前缀匹配查询或完全匹配的查询。

显然,您的第一个查询不符合条件。你不需要为此提供黑客攻击。相反,您可以提示优化器使用{a:1,b:1}索引

db.Collection.find({ b: 1 }).hint({ a:1, b:1 })

答案 2 :(得分:0)

如果您的电话簿按“姓氏,名字”排列,但您只有名字,您认为电话簿会帮助您找到您要搜索的人吗?

当你在a,b上有索引并且你正在选择b时,这就是你试图强制优化器做的事情。这意味着它需要查看并查看b是否匹配。

在某些情况下,使用此索引可能比收集扫描更快的原因有很多。一般来说,它不是候选索引,您不应该将其用作加速b上的查询的解决方案。

the current version's MongoDB query optimizer works的方式是尝试使用多个查询计划(所有候选索引加上集合扫描)的查询。无论哪个是最快的“胜利”,其他人都会被终止,并且获胜计划会被缓存一段时间。如果你运行`db.collection.find(...)。explain(true),你实际上会看到它尝试过的所有“计划”。如果索引不被视为候选者,那么它将不会处于混合阶段 - 获得查询使用它的唯一方法是明确地“暗示”它。

查询优化器将在下一个主要版本中进行更改,因此上述内容适用于2.4及更早版本的世界状态。