【问题标题】:MongoDB query optimizer keeps choosing the least efficient index for the queryMongoDB 查询优化器不断为查询选择效率最低的索引
【发布时间】:2021-01-23 16:42:46
【问题描述】:

我有一个大型集合(约 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 ms

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

为什么查询优化器总是选择最慢的索引?看起来很可疑,它只发生在高峰时段,当集合有更多的写入活动时,但确切的原因是什么?我该怎么办?

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

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

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

【问题讨论】:

    标签: mongodb performance optimization indexing


    【解决方案1】:

    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 成为第一个,这取决于哪个字段具有更高的基数。基数基本上意味着给定字段中所有值的唯一性。如果集合中的每个名称都是不同的,那么您可能希望 name 排在第一位。另一方面,如果每个created_at 时间戳都应该是唯一的,那么将那个字段放在第一位可能是有意义的。至于active,它看起来是一个布尔字段,因此只能取两个值(真/假)。它应该在索引中的最后一个(您甚至可以完全省略它)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-30
      • 2015-03-13
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-20
      相关资源
      最近更新 更多