MongoDB/AWS documentdb索引利用率有限制和无限制

MongoDB/AWS documentdb索引利用率有限制和无限制,mongodb,indexing,aws-documentdb,Mongodb,Indexing,Aws Documentdb,这是我的索引: db.foobar.createIndex({foo':-1,'bar':1,'baz':1},{background:true,name:'foobar_idx'}) 现在我希望一个按foo排序并在bar上过滤的查询将使用索引。如果你指定了一个限制,它会执行: rs0:PRIMARY> db.foobar.find( { 'bar' : 'xyz' }, { 'some.field' : 1 } ).sort( { 'foo' : -1 } ).limit(1000).e

这是我的索引:

db.foobar.createIndex({foo':-1,'bar':1,'baz':1},{background:true,name:'foobar_idx'})

现在我希望一个按
foo
排序并在
bar
上过滤的查询将使用索引。如果你指定了一个限制,它会执行:

rs0:PRIMARY> db.foobar.find( { 'bar' : 'xyz' }, { 'some.field' : 1 } ).sort( { 'foo' : -1 } ).limit(1000).explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "foobardb",
                "winningPlan" : {
                        "stage" : "SUBSCAN",
                        "inputStage" : {
                                "stage" : "LIMIT_SKIP",
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "indexName" : "foobar_idx",
                                        "direction" : "forward"
                                }
                        }
                }
        },
        "ok" : 1
}
但如果未指定限制,或限制非常高,则不希望使用索引:

rs0:PRIMARY> db.foobar.find( { 'bar' : 'xyz' }, { 'some.field' : 1 } ).sort( { 'foo' : -1 } ).explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "foobardb",
                "winningPlan" : {
                        "stage" : "SUBSCAN",
                        "inputStage" : {
                                "stage" : "SORT",
                                "sortPattern" : {
                                        "foo" : -1
                                },
                                "inputStage" : {
                                        "stage" : "COLLSCAN"
                                }
                        }
           },
        "ok" : 1
}
即使我提供了使用索引的提示,它也不会使用它


它究竟为什么不使用darn索引?

如果索引的选择性不足,那么表扫描可能比索引扫描更有效。存储系统也影响决策(旋转磁盘有利于表扫描,SSD支持索引扫描)。

< P>为了理解这种行为,您必须考虑如何构造索引,以及如何搜索索引。 考虑包含以下10个文档的集合:

{"foo" : 9, "bar" : "A", "baz" : "Y" }
{"foo" : 2, "bar" : "B", "baz" : "Y" }
{"foo" : 5, "bar" : "A", "baz" : "Z" }
{"foo" : 0, "bar" : "A", "baz" : "Y" }
{"foo" : 6, "bar" : "A", "baz" : "X" }
{"foo" : 4, "bar" : "B", "baz" : "Y" }
{"foo" : 8, "bar" : "A", "baz" : "Z" }
{"foo" : 1, "bar" : "A", "baz" : "Y" }
{"foo" : 7, "bar" : "B", "baz" : "Z" }
{"foo" : 3, "bar" : "B", "baz" : "X" }
如果我们在
{foo:1,bar:1,baz:1}
上定义索引,索引将包含以下对:

0|A|Y => 3
1|A|Y => 7
2|B|Y => 1
3|B|X => 9
4|B|Y => 5
5|A|Z => 2
6|A|X => 4
7|B|Z => 8
8|A|Z => 6
9|A|Y => 0
平等查询

如果我们随后查询
{foo:5,bar:“A”}
,则查询执行器可以开始扫描第一个匹配值
5 | A | Z
。在本例中,它是唯一匹配的值,因此结束于此

范围查询

如果我们随后查询
{foo:{$lt:5},bar:“A”}
,它将扫描
[MinKey()范围内的
foo
值的索引,5)
,对于遇到的
foo
的每个值,它将对
条的匹配值进行扫描。这意味着它不需要扫描索引的单个范围,而需要扫描5个范围以找到2个匹配项

查询+排序

如果我们在
{bar:“A”}
上查询并按
{foo:1}
排序,如果查询执行器尝试使用此索引,它将需要检查索引中的每个条目,并且对于
foo
的每个值,都要扫描
bar
的匹配值。对于本例,这意味着10个范围

查询计划

当第一次看到查询形状时,查询计划器会确定它可能运行查询的不同方式,并运行测试。每个计划都会在短时间内运行,并选择以最少工作量产生最多结果的计划

db.foobar.find({bar:A}).sort({foo:1})
的情况下,我们的测试场景有两种可能的计划:

计划A:索引扫描

  • 从磁盘加载索引(如果尚未在缓存中)
  • 扫描10个索引范围
  • 从磁盘加载6个文档(如果尚未在缓存中)
计划B:收集扫描

  • 从磁盘加载10个文档(如果尚未在缓存中)
  • 在内存中排序
根据缓存中已经存在的内容,这里的选择有点困难

使用限制

当您引入一个较小的限制时,例如
db.foobar.find({bar:“a”}).sort({foo:1}).limit(2)
,当使用索引查找已按排序顺序排列的文档时,查询可以提前终止。在这种情况下,可能的计划如下所示:

计划A:索引扫描

  • 从磁盘加载索引(如果尚未在缓存中)
  • 扫描2个索引范围
  • 从磁盘加载2个文档(如果尚未在缓存中)
计划B:收集扫描

  • 从磁盘加载10个文档(如果尚未在缓存中)
  • 在内存中排序
  • 最多2份文件
很明显,在这种情况下,索引扫描的性能会更好

有更大的限制,这不是很明显。考虑<代码> db .FooBar。查找({Bar:a)}。排序({Fo:1 })。限制(5)< /代码>,对于这个查询,可能的计划是:

计划A:索引扫描

  • 从磁盘加载索引(如果尚未在缓存中)
  • 扫描9个索引范围
  • 从磁盘加载5个文档(如果尚未在缓存中)
计划B:收集扫描

  • 从磁盘加载10个文档(如果尚未在缓存中)
  • 在内存中排序
  • 最多5份文件
这几乎回到了与无限案例相同的计划

更好的指数

当在MangGDB中建立索引时,考虑如何计划查询数据,并根据均等排序范围对索引中的键进行排序。这意味着列出要匹配的字段,然后列出排序字段,然后再列出其他字段。 对于我们的示例,

{bar:1,foo:1,baz:1}
上的索引将包含以下对:

A|0|Y => 3
A|1|Y => 7
A|5|Z => 2
A|6|X => 4
A|8|Z => 6
A|9|Y => 0
B|2|Y => 1
B|3|X => 9
B|4|Y => 5
B|7|Z => 8
排序查询
db.foobar.find({bar:A}).sort({foo:1})
将有另一个可能的计划:

方案C:索引扫描

  • 扫描
    {bar:1,foo:1,baz:1}
    索引的单个范围
  • 从磁盘获取6个文档(如果尚未在缓存中)

这个计划应该比所有其他的可能性都好很多,并且应用一个限制会减少这个计划所做的工作,所以它仍然应该被选择。

谢谢你,我不清楚你索引示例中的数字指向什么:
B | 2 | Y=>1
1代表什么?我的文档也是多兆字节的每个文件都有数百万个,因此如果需要排序和筛选的所有内容都在索引中,我看不出collscan是一个更好的计划。1代表内部文档标识符,在本例中,我只是按照它们出现的顺序对原始10个文档进行编号。