在 MongoDB 中按字段高效分组数百万条记录 - 性能优化

问题描述 投票:0回答:1

我正在处理 MongoDB 中的大型数据集,我需要按特定字段(名称)对大约 1500 万条记录进行分组。目标是找到名称字段重复的所有记录。我目前正在使用以下聚合管道:

[
  {
    "$group": {
      "_id": "$name",
      "count": { "$sum": 1 },
      "records": { "$push": "$$ROOT" }
    }
  },
  {
    "$match": {
      "count": { "$gt": 1 }
    }
  }
]

此查询对于较小的数据集效果很好,但对于如此大量的记录,它就会出现问题。我遇到性能问题,包括内存使用率高和执行时间长。

我尝试过的: 索引:我确保对名称字段建立索引以优化组操作。 allowDiskUse:我启用了allowDiskUse选项以允许MongoDB使用磁盘上的临时文件,这有助于大型聚合,但性能仍然不理想。

我的问题: 有没有更有效的方法在 MongoDB 中对如此大的数据集执行这种分组操作? 我是否应该注意处理如此规模的数据的特定技术或最佳实践? 对集合进行分片是否会提高此类查询的性能?如果是,我应该如何处理? 是否有任何 MongoDB 功能或替代方法(例如 MapReduce、批处理)可以帮助更有效地管理此操作?

mongodb performance group-by mongodb-query
1个回答
0
投票

查询读取的字段多于索引中的字段,因此它必须从磁盘获取所有文档才能满足查询。

小组赛阶段将整个原始文档存储在一个数组中,因此除了从磁盘获取所有文档(这可能会导致缓存压力)之外,此查询还需要在内存中同时保存所有 1500 万个文档。

您可以通过在

{name:1, _id:1}
上创建索引并返回 _id 值数组而不是完整文档数组来减少此查询所需的磁盘带宽和内存。

使用该索引,类似以下的查询将:

  • 使用索引
    {name:1, _id:1}
    被索引完全覆盖,而不是存储 1500 万个完整文档,而是存储 1500 万个 ObjectId(总共约 250MB)。
[
  { 
    "$match": {
       "name": {"$ne": null}
    }
  },
  {
    "$group": {
      "_id": "$name",
      "count": { "$sum": 1 },
      "records": { "$push": "$_id" }
    }
  },
  {
    "$match": {
      "count": { "$gt": 1 }
    }
  }
]

使用Explain检查:https://mongoplayground.net/p/o3z6mkiwNwr显示查询使用了索引和PROJECTION_COVERED,并且没有检查任何文档

无需解释:https://mongoplayground.net/p/oh065DKBpzh该查询几乎立即扫描 1M 文档。

© www.soinside.com 2019 - 2024. All rights reserved.