假设我有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
忽略我的查询中的索引? < /强>
答案 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及更早版本的世界状态。