MongoDB 查询优化器不断为查询选择效率最低的索引

标签 mongodb performance optimization indexing

我有一个很大的集合(约 20M 条记录),其中有一些中等文档,其中有约 20 个索引字段。所有这些索引都是单个字段。该集合也有相当多的写入和读取流量。

MongoDB 版本是 4.0.9。

我发现在高峰时段,查询优化器不断为获胜计划选择非常低效的索引。

在示例查询中:

{
  name: 'Alfred Mason',
  created_at: { $gt: ... },
  active: true
}

所有字段均已编入索引:

{ name: 1 }
{ created_at: 1 }
{ active: 1 }

当我运行explain()时,获胜计划将使用created_at索引,该索引将在返回之前扫描~200k文档 4 与查询匹配。查询执行时间为~6000 毫秒

如果我使用 $hint 强制 name 索引,它将在返回 4 之前扫描 6 个文档与查询匹配的。执行时间为~2 ms

为什么查询优化器不断选择最慢的索引?确实令人怀疑的是,它只发生在高峰时段,此时集合有更多的写入事件,但确切的原因是什么?我能做什么呢?

在生产环境中使用$hint安全吗?

完全删除日期字段上的索引是否合理,因为 $gt 查询似乎并不比 COLLSCAN 更快?这可能会强制查询优化器使用索引字段。但话又说回来,它也可能选择另一个低效索引( bool 字段)。

我无法使用复合索引,因为有很多用例使用所有 20 个可用索引的不同组合。

最佳答案

Mongo 似乎没有使用最佳执行计划可能有多种原因,包括:

  • 使用 name 上的单字段索引估算运行时间和执行计划字段不准确。这可能是由于错误的统计数据造成的,即 Mongo 使用过时或不是最新的信息进行估计。
  • 对于您的特定查询,created_at索引并不是最优的,一般来说,对于该字段上的大多数可能的查询,created_at索引将是最佳的。

我的回答实际上是,考虑到您要过滤多个字段,您可能应该使用多字段索引。对于您在问题中给出的示例过滤器:

{
  name: 'Alfred Mason',
  created_at: { $gt: ... },
  active: true
}

我建议尝试以下两个索引:

db.getCollection('your_collection').createIndex(
    { "name": 1, "created_at": 1, "active": 1 } );

db.getCollection('your_collection').createIndex(
    { "created_at": 1, "name": 1, "active": 1 } );

您是否想要created_at成为索引中的第一名,或者更确切地说 name成为第一,取决于哪个字段具有更高的基数。基数基本上意味着给定字段中所有值的唯一性。如果集合中的每个名称都是不同的,那么您可能希望名称位于第一个。另一方面,如果每个 created_at时间戳预计是唯一的,那么将该字段放在第一位可能是有意义的。至于active ,它似乎是一个 bool 字段,因此只能采用两个值(真/假)。它应该位于索引的最后(您甚至可以完全省略它)。

关于MongoDB 查询优化器不断为查询选择效率最低的索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64265835/

相关文章:

javascript - 错误: TypeError: Cannot read property 'catch' of undefined when trying to register user for website node.js

mongodb - 如何按小时对数据进行分组

iphone - Xcode/Objective-C : Why is NSTimer sometimes slow/choppy?

c++ - "high involuntary context"开关是什么意思?

c++ - 优化 C++ 代码(使用 UnorderedMap 和 Vector)

java - MongoDb 多重排序

java - String.lastIndexOf() char 和具有单个字符的字符串之间有区别吗?

c - 如何处理和优化 C 代码

MongoDB 查询超时

mongodb - mongodb 中哪个消耗更多内存 bool 值或整数?