如果你有一个双复合索引{a : 1, b : 1},那么如果你单独查询b,这个索引不会被使用对我来说是有意义的(即你不能在查询中"跳过"a)。但是,如果单独查询,则将使用该索引。
但是,给定一个三重复合索引 { a : 1, b: 1, c: 1} 我的 explain 命令显示当您查询 a 和 c 时会使用该索引(即您可以在查询中"跳过"b)。
Mongo 如何在查询 ac 时使用 abc 索引,在这种情况下索引的效果如何?
背景:
我的用例是,有时我想查询a,b,c,有时我想查询a,c。现在我应该只在 a,b,c 上创建一个索引,还是应该在 a,c 上创建一个索引,在 a,b,c 上创建一个索引?
(在 a,c,b 上创建索引是没有意义的,因为 c 是具有良好选择性的多键索引。
底线/tl;dr:如果查询a
和c
相等或不相等,则可以"跳过"索引b
,但不能c
上的排序。
这是一个非常好的问题。不幸的是,我找不到任何权威地更详细地回答这个问题的东西。我相信此类查询的性能在过去几年中有所提高,所以我不会相信有关该主题的旧材料。
整个事情非常复杂,因为它取决于索引的选择性以及您是否查询相等、不等式和/或排序,所以explain()
是您唯一的朋友,但以下是我发现的一些事情:
警告 :现在出现的是实验结果,推理和猜测的混合体。我可能把凯尔的类比拉得太远了,我甚至可能完全错了(而且很不幸,因为我的测试结果与我的推理松散地吻合)。
很明显,可以使用 A 的索引,这取决于 A 的选择性,这当然是非常有帮助的。"跳过"B 可能很棘手,也可能不。让我们保持与 Kyle 的食谱示例类似:
French
Beef
...
Chicken
Coq au Vin
Roasted Chicken
Lamb
...
...
如果你现在让我找一些叫做"夏多布里昂"的法国菜,我可以使用索引A
,因为我不知道成分,所以必须扫描A
中的所有菜肴。另一方面,我确实知道每个类别中的菜肴列表都是通过索引C
排序的,所以我只需要在每个成分表中查找以"Cha"开头的字符串。如果有 50 种食材,我将需要 50 次查找,而不仅仅是一次,但这比扫描每道法国菜要好得多!
在我的实验中,这个数字比
b
中的非重复值的数量要小得多:它似乎永远不会超过 2。但是,我只用一个集合对此进行了测试,这可能与b
索引的选择性有关。
不过,如果你让我给你一个按字母顺序排列的所有法国菜肴的清单,我会有麻烦的。现在C
上的索引毫无价值,我必须合并排序所有这些索引列表。我将不得不扫描每个元素才能这样做。
这反映在我的测试中。以下是一些简化的结果。原始集合有日期时间、整数和字符串,但我想保持简单,所以现在都是整数。
本质上,只有两类查询:nscanned
<= 2 * limit
的查询和必须扫描整个集合(120k 文档)的查询。该指数{a, b, c}
:
// fast (range query on c while skipping b)
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }});
// slow (sorting)
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "c" : -1});
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "b" : -1});
// fast (can sort on c if b included in the query)
> db.Test.find({"a" : 43, "b" : 7887, "c" : { $lte : 45454 }}).sort({ "c" : -1});
// fast (older tutorials claim this is slow)
> db.Test.find({"a" : {$gte : 43}, "c" : { $lte : 45454 }});
您的里程会有所不同。
您可以将对 A 和 C 的查询视为对 A 进行查询的特殊情况(在这种情况下将使用索引)。使用索引比加载整个文档更有效。
假设您要获取 A 介于 7 和 13 之间,C 介于 5 和 8 之间的所有文档。
如果您仅在 A 上有一个索引:数据库可以使用索引来选择介于 7 和 13 之间的 A 的文档,但为了确保 C 在 5 到 8 之间,它也必须检索相应的文档。
如果您有 A、B 和 C 上的索引:数据库可以使用该索引来选择 A 介于 7 和 13 之间的文档。由于C的值已经存储在索引的记录中,因此它可以确定相应的文件是否也符合C标准,而不必检索这些文件。因此,您可以避免磁盘读取,从而获得更好的性能。