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



我有一个大型集合(约2000万条记录(,其中包含一些中等大小的文档,其中包含约20个索引字段。所有这些索引都是单个字段。这个集合也有相当多的写和读流量。

MongoDB版本为4.0.9。

我看到在高峰期,查询优化器不断为获胜计划选择一个效率非常低的索引。

在示例查询中:

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

所有字段都已编入索引:

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

当我运行explain()时,获胜计划将使用created_at索引,该索引将扫描~200000个文档,然后返回匹配查询的4。查询执行时间~6000毫秒

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

为什么查询优化器总是选择最慢的索引?这似乎很可疑,因为它只发生在高峰时段,当收藏有更多的写作活动时,但确切的原因是什么?我该怎么办?

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

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

我不能使用复合索引,因为有很多用例使用所有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,它看起来是一个布尔字段,因此只能有两个值(true/false(。它应该是索引中的最后一个(您甚至可以完全省略它(。

我认为没有必要对所有字段进行索引,最好选择合适的字段。

复合索引中的前缀可能对有用

相关内容

最新更新