You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速百万级文档的MongoDB聚合查询?

MongoDB百万级订单集合Top3销量产品聚合优化方案

问题分析

从执行计划能看出,当前聚合的核心瓶颈是全表扫描(COLLSCAN),需要读取全部100万+文档,这占了大部分耗时;其次是$unwind后生成1100万+条中间数据,进一步增加了处理负载。常规索引未生效是因为没针对聚合所需字段创建覆盖索引,导致MongoDB无法跳过全表扫描。

优化步骤

1. 创建覆盖索引

创建包含聚合所需字段的覆盖索引,让MongoDB直接从索引中读取数据,避免全表扫描:

db.orders.createIndex(
  { "products._id": 1, "products.quantity": 1 },
  { projection: { _id: 0 } }
)
  • 索引包含products._id和products.quantity,刚好匹配聚合中分组和求和的需求
  • projection: { _id: 0 }指定不包含_id字段,进一步缩小索引体积

2. 简化聚合管道

移除不必要的$project阶段(覆盖索引已自动过滤所需字段),简化后的管道如下:

[
  { $unwind: { path: '$products' } },
  {
    $group: {
      _id: '$products._id',
      unitsSold: { $sum: '$products.quantity' }
    }
  },
  { $sort: { unitsSold: -1 } },
  { $limit: 3 }
]
  • 移除$project减少一个处理阶段,降低内存开销
  • 保留核心逻辑:展开数组→按产品ID分组求和→排序取Top3

3. 预聚合(非实时场景最优解)

如果不需要实时统计销量,建议定期预聚合结果到单独集合,彻底解决实时聚合的性能问题:

步骤1:创建预聚合脚本

// 定期执行(如通过MongoDB定时任务或外部脚本)
db.product_sales.deleteMany({}); // 清空旧数据
db.orders.aggregate([
  { $unwind: { path: '$products' } },
  {
    $group: {
      _id: '$products._id',
      unitsSold: { $sum: '$products.quantity' },
      productName: { $first: '$products.name' } // 可选,存储产品名称方便查询
    }
  },
  { $out: 'product_sales' } // 将结果写入product_sales集合
]);

步骤2:快速查询Top3

db.product_sales.find().sort({ unitsSold: -1 }).limit(3)
  • 预聚合后查询仅需毫秒级响应,适合报表、后台统计等非实时场景

效果验证

创建覆盖索引后,执行计划中的COLLSCAN会变为IXSCAN(索引扫描),totalDocsExamined会大幅降低,聚合耗时可缩短至数秒内;预聚合方案则能实现亚毫秒级查询。

内容的提问来源于stack exchange,提问作者m3k_1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 23:32:15