我正在处理 MongoDB 中的大型数据集,我需要按特定字段(名称)对大约 1500 万条记录进行分组。目标是找到名称字段重复的所有记录。我目前正在使用以下聚合管道:
[
{
"$group": {
"_id": "$name",
"count": { "$sum": 1 },
"records": { "$push": "$$ROOT" }
}
},
{
"$match": {
"count": { "$gt": 1 }
}
}
]
此查询对于较小的数据集效果很好,但对于如此大量的记录,它就会出现问题。我遇到性能问题,包括内存使用率高和执行时间长。
我尝试过的: 索引:我确保对名称字段建立索引以优化组操作。 allowDiskUse:我启用了allowDiskUse选项以允许MongoDB使用磁盘上的临时文件,这有助于大型聚合,但性能仍然不理想。
我的问题: 有没有更有效的方法在 MongoDB 中对如此大的数据集执行这种分组操作? 我是否应该注意处理如此规模的数据的特定技术或最佳实践? 对集合进行分片是否会提高此类查询的性能?如果是,我应该如何处理? 是否有任何 MongoDB 功能或替代方法(例如 MapReduce、批处理)可以帮助更有效地管理此操作?
查询读取的字段多于索引中的字段,因此它必须从磁盘获取所有文档才能满足查询。
小组赛阶段将整个原始文档存储在一个数组中,因此除了从磁盘获取所有文档(这可能会导致缓存压力)之外,此查询还需要在内存中同时保存所有 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 文档。