MongoDB千万级文档集合高效统计字段去重数量的方法
高效统计MongoDB大集合指定字段的去重数量
有一个包含超1000万条文档的MongoDB集合,文档结构如下:
[ { //-- type and date "date": "2023-11-09", "type": "my_type", "resource": "2222" }, { //-- type and date "date": "2023-11-09", "type": "my_type", "resource": "1111" }, { //-- type and date "date": "2023-11-09", "type": "my_type", "resource": "1111" } ]
需要实现性能优异的查询,统计resource字段的去重数量,要求不可预计算统计结果,但可以给resource字段添加索引。
当前使用的聚合查询(存在性能与逻辑问题)
[ { $sort: { resource: 1, }, }, { $group: { _id: null, distinctResources: { $addToSet: "$resource", }, }, }, { $project: { total: { $size: ["$resource"], }, }, }, ]
问题说明:
$addToSet会将所有去重值加载到内存,千万级数据下极易触发内存溢出;$project中的$size引用错误,应指向$distinctResources而非$resource;额外的$sort也会增加不必要的性能开销。
期望输出结果
{ "_id": null, "totalDistinct": 2 // 对应示例中的1111和2222 }
优化方案
1. 先给resource字段创建单键索引
这是提升查询性能的核心前提,索引可让MongoDB直接扫描索引键,避免全表扫描:
db.yourCollection.createIndex({ resource: 1 })
2. 最优聚合查询(内存友好+性能优异)
使用$group按resource分组,再用$count统计分组数量,这种方式仅遍历索引中的唯一值,无需加载大量数据到内存,完全适配千万级数据集:
[ { $group: { _id: "$resource" } }, { $count: "totalDistinct" } ]
备选方案(MongoDB 5.0+支持)
若需使用窗口函数实现,可参考以下写法,但内存占用高于上述方案:
[ { $sort: { resource: 1 } }, { $setWindowFields: { partitionBy: null, sortBy: { resource: 1 }, output: { distinctCount: { $addToSet: "$resource", window: { documents: ["unbounded", "current"] } } } } }, { $skip: { $subtract: [{ $count: {} }, 1] } }, { $project: { totalDistinct: { $size: "$distinctCount" }, _id: 0 } } ]
不推荐的方式
distinct命令会返回所有去重值的数组,千万级数据下极易触发内存溢出,仅适合小数据量场景:
// 大数据量禁用 db.yourCollection.distinct("resource").length
内容的提问来源于stack exchange,提问作者Moussa ELaQ
相关产品推荐
相关产品推荐

