【问题标题】:Mongo using the wrong index during aggregation with match+sort operationsMongo 在使用匹配+排序操作进行聚合期间使用了错误的索引
【发布时间】:2020-01-14 15:53:20
【问题描述】:

我正在使用 MongoDB 版本 4.2.0。我有一个包含以下索引的集合:

{uuid: 1},
{unique: true, name: "uuid_idx"}

和

{field1: 1, field2: 1, _id: 1},
{unique: true, name: "compound_idx"}

执行此查询时

aggregate([
  {"$match": {"uuid": <uuid_value>}}
])

规划者正确选择uuid_idx。

添加此排序子句时

aggregate([
  {"$match": {"uuid": <uuid_value>}},
  {"$sort": {"field1": 1, "field2": 1, "_id": 1}}
])

规划器选择compound_idx,这会使查询变慢。

我希望 sort 子句在这种情况下不会产生影响。 为什么 Mongo 在这两种情况下都不使用uuid_idx 索引?

编辑: 稍微澄清一下,我知道有一些解决方法可以使用正确的索引,但我正在寻找解释为什么这不会自动发生(如果可能的话,带有官方文档的链接)。谢谢!

【问题讨论】:

  • 如果有人想知道,查询是动态生成的,目前排序子句是自动插入的。
  • 第二个聚合查询选择了多少文档(具有匹配和排序阶段的文档)?

标签: mongodb indexing aggregation-framework


【解决方案1】:

为什么会这样?:

让我们了解 Mongo 如何按照 here 的说明选择要使用的索引。

如果一个查询可以通过集合中定义的多个索引来满足(满足时会丢失,因为 Mongo 实际上会选择所有可能相关的索引)。

然后,MongoDB 将并行测试所有适用的索引。查询计划器将选择第一个可以返回 101 个结果的索引。

意味着对于该特定查询,索引实际上胜出。

我们能做什么?:

我们可以使用$hint,hint 基本上是强制 Mongo 使用特定的索引,但是 Mongo 不建议这样做,因为如果发生变化,Mongo 将无法适应这些。

【讨论】:

  • 感谢您的回答,但我仍然很困惑。您的第一个链接指的是 2.6 版,我找不到 4.2 版的相同解释:它仍然适用吗?此外,从该链接看来,所选索引要么返回所有匹配结果,要么比所有其他索引更快地返回 101 个结果。难道uuid_idx返回一个匹配比compound_idx返回101个匹配慢?
【解决方案2】:

查询:

aggregate( 
  [
    { $match : { uuid : "some_value" } },
    { $sort : { fld1: 1, fld2: 1, _id: 1 } }
  ],
)

不使用索引“uuid_idx”。

您可以使用几个选项在 match 和 sort 操作中使用索引:


(1) 定义一个新的复合索引:{ uuid: 1, fld1: 1, fld2: 1, _id: 1 }

match 和 match+sort 查询都将使用此索引(用于匹配和排序操作)。


(2) 在 uuid 索引上使用提示(使用现有索引)

match 和 match+sort 查询都将使用此索引(用于匹配和排序操作)。

aggregate( 
  [
    { $match : { uuid : "some_value" } },
    { $sort : { fld1: 1, fld2: 1, _id: 1 } }
  ],
  { hint: "uuid_idx"}
)

【讨论】:

  • 你是说planner总是优先考虑sort子句上的索引,即使match子句上有100%选择性的索引?官方文档中有关于这种行为的参考吗?
  • 不,sort 操作没有这样的优先级。一般来说,在同一个查询上使用两个索引并不是最佳实践;如果查询可以使用一个复合索引进行匹配和排序操作,那么它是最佳选择。
  • 查询优化器根据可用和可行的索引(又名候选索引)生成多个计划。 MongoDB 使用经验(基于观察或经验)查询优化器。每个候选计划都会在短时间内执行。优化器会查看在此期间哪个计划执行得最好(最佳由各种因素决定,例如最快、更多文档,甚至一个计划可能在试用期间完成,阈值等)。另请参阅Query plans and caching。
  • 我尝试了以下查询,发现它使用了uuid_idx(不是聚合中的复合索引):db.colln.find( {"uuid": &lt;somevalue&gt; } ).sort({ fld1: 1, fld2: 1, _id: 1 })。而且,排序发生在内存中。
  • 但是,当四个字段的复合索引应用于 find.sort 查询时,两个操作都使用索引。
【解决方案3】:

如果您可以使用 find 而不是聚合,它将使用正确的索引。所以这仍然是聚合管道中的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多