底线 / 简而言之: 如果查询等式或不等式时,可以“跳过”索引
b
,但是在对
c
进行排序时不行。
这是一个非常好的问题。不幸的是,我没有找到任何权威的答案来详细回答这个问题。我相信在过去几年中,此类查询的性能有所提高,因此我不会信任有关此主题的旧材料。
整个问题相当复杂,因为它取决于您的索引选择性以及您是否查询等式、不等式和/或排序,所以explain()
是您唯一的朋友,但是这里有一些我发现的东西:
注意:接下来的内容是实验结果、推理和猜测的混合物。我可能过分解释了Kyle的比喻,并且我甚至可能完全错了(并且不幸的是,我的测试结果与我的推理大致匹配)。
很明显可以使用A的索引,这取决于A的选择性,肯定非常有帮助。跳过B可能会有些棘手,也可能不会。让我们将其保持类似于Kyle's cookbook example:
French
Beef
...
Chicken
Coq au Vin
Roasted Chicken
Lamb
...
...
如果你现在让我找一道名为“香槟牛排”的法国菜肴,我可以使用索引
A
,但由于我不知道该菜肴的成分,我将不得不扫描
A
中的所有菜肴。另一方面,我知道每个类别的菜肴列表都是通过索引
C
排序的,因此我只需要查找以“Cha”开头的字符串所在的每个成分列表。如果有50种成分,则我需要进行50次查找,而不是扫描每一个法国菜肴!
在我的实验中,这个数字比b
中不同值的数量小得多:它似乎从未超过2。然而,我只使用了单个集合进行测试,它可能与b
-index的选择性有关。
如果你让我给你一个按字母顺序排序的所有法国菜肴列表,那么我就麻烦了。现在
C
上的索引毫无用处,我必须合并排序所有这些索引列表。我将不得不扫描每个元素来执行此操作。
这在我的测试中得到了体现。以下是一些简化的结果。原始集合包含日期时间、整数和字符串,但为了保持简单,现在全部都是整数。
基本上,只有两类查询:那些
nscanned
<= 2 *
limit
的查询,以及必须扫描整个集合(120k文档)的查询。索引是
{a, b, c}
:
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }});
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "c" : -1});
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "b" : -1});
> db.Test.find({"a" : 43, "b" : 7887, "c" : { $lte : 45454 }}).sort({ "c" : -1});
> db.Test.find({"a" : {$gte : 43}, "c" : { $lte : 45454 }});
你的里程可能会有所不同。
explain()
可以告诉你确切的情况。请注意nscanned
,它不应该比n
大太多。 - mnemosyn