我们最近决定重新访问一些MongoDB索引,并在使用包含多键部分的复合索引时遇到了一个奇怪的结果.
值得注意的是,我们正在使用v2.4.5
TLDR:当使用具有多键部分的复合索引时,将删除用于范围限制的非多键字段的边界.
我将用一个例子来解释这个问题:
创建一些数据
db.demo.insert(
[{ "foo" : 1, "attr" : [ { "name" : "a" }, { "name" : "b" }, { "name" : "c" } ]},
{ "foo" : 2, "attr" : [ { "name" : "b" }, { "name" : "c" }, { "name" : "d" } ]},
{ "foo" : 3, "attr" : [ { "name" : "c" }, { "name" : "d" }, { "name" : "e" } ]},
{ "foo" : 4, "attr" : [ { "name" : "d" }, { "name" : "e" }, { "name" : "f" } ]}])
Run Code Online (Sandbox Code Playgroud)
指数
db.demo.ensureIndex({'attr.name': 1, 'foo': 1})
Run Code Online (Sandbox Code Playgroud)
查询和解释
查询'attr.name'但约束非多键字段'foo'的范围:
db.demo.find({foo: {$lt:3, $gt: 1}, 'attr.name': 'c'}).hint('attr.name_1_foo_1').explain()
{
"cursor" : "BtreeCursor attr.name_1_foo_1",
"isMultiKey" : true,
"n" : 1,
"nscannedObjects" : 2,
"nscanned" : 2,
"nscannedObjectsAllPlans" : 2,
"nscannedAllPlans" : 2,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"attr.name" : [
[
"c",
"c"
]
],
"foo" : [
[
-1.7976931348623157e+308,
3
]
]
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,'foo'的范围不是在查询中定义的,一端被完全忽略,这导致nscanned比它应该更大.
更改范围操作数的顺序将改变删除的结尾:
db.demo.find({foo: {$gt: 1, $lt:3}, 'attr.name': 'c'}).hint('attr.name_1_foo_1').explain()
{
"cursor" : "BtreeCursor attr.name_1_foo_1",
"isMultiKey" : true,
"n" : 1,
"nscannedObjects" : 2,
"nscanned" : 2,
"nscannedObjectsAllPlans" : 2,
"nscannedAllPlans" : 2,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"attr.name" : [
[
"c",
"c"
]
],
"foo" : [
[
1,
1.7976931348623157e+308
]
]
}
}
Run Code Online (Sandbox Code Playgroud)
我们要么错过了一些多键索引基础知识,要么就是我们遇到了一个bug.
我们经历过类似的话题,包括:
不幸的是,这些帖子解决了一个不同的用例,其中在多键值上设置了范围.
我们尝试过的其他事情:
更改复合索引排序,从非多键字段开始.
将'foo'值放在'attr'数组的每个子文档中,索引('attr.name','attr.foo')并在'attr'上执行$ elemMatch,并在'foo'上设置范围约束.
定义范围时使用$和运算符:
db.demo.find({'attr.name': 'c', $and: [{num: {$lt: 3}}, {num: {$gt: 1}}]})
Run Code Online (Sandbox Code Playgroud)使用MongoDB v2.5.4
以上都没有任何影响(v2.5.4通过完全倾倒范围的两端使事情变得更糟).
任何形式的帮助将非常感谢!
非常感谢,
投资回报率
对于复合索引,其中索引字段之一是数组,MongoDB 将仅使用范围查询的下限或上限,以确保返回正确的匹配项。请参阅SERVER-958的示例,其中限制索引上限和下限将找不到预期的文档。
如果您的范围查询位于数组字段上,您可以使用$elemMatch运算符在预期索引范围内优化查询。与 MongoDB 2.4 一样,该$elemMatch运算符不适用于非数组字段,因此不幸的是,这对您的用例没有帮助。您可以观看/投票SERVER-6050:考虑允许 $elemMatch 应用于MongoDB 问题跟踪器中的非数组。
还有一个未解决的问题SERVER-7959:当某些字段是描述此行为的多键时,可能会使用复合索引进行意外扫描。
| 归档时间: |
|
| 查看次数: |
1279 次 |
| 最近记录: |