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

MongoDB 5.0.20聚合管道性能优化求助:3万数据耗时20+秒

优化建议与解决方案

1. 修复日期查询,让索引生效

当前日期查询使用$expr+$dateFromString的方式,无法利用createdAt字段的索引,导致初始查询阶段耗时较高(explain里$cursor阶段花了4925ms)。改用代码中注释掉的方式,提前在应用层将日期字符串转换为Date对象:

if (filters.startDate) {
  startDateQuery = { 
    createdAt: { 
      $gte: DateTime.fromFormat(filters.startDate, "MM-dd-yyyy").setZone("+05:30").toJSDate() 
    } 
  }
}
if (filters.endDate) {
  endDateQuery = { 
    createdAt: { 
      $lt: DateTime.fromFormat(filters.endDate, "MM-dd-yyyy").plus({ days: 1 }).setZone("+05:30").toJSDate() 
    } 
  }
}

这样createdAt的索引能被直接利用,大幅减少初始扫描的文档数和耗时。

2. 提前过滤数据,减少后续处理压力

第二个$match中的部分条件可以提前到第一个$match中执行,避免对所有lookup后的文档进行过滤:

  • cidNumberSearchQuery:commodityDetail.CIDNumber是原集合字段,直接移到第一个$match的$and数组里
  • lotNoSearchQuery:commodityDetail.LOTNumber也是原集合字段,同样移到第一个$match中

对于基于lookup后数据的过滤(如commodityData.name、commodityVariantData.name),可以将过滤逻辑嵌入到对应的$lookup的pipeline中,提前筛选关联文档,减少返回的数据集:

// 示例:修改commodity的lookup,加入名称过滤
{
  $lookup: {
    from: 'mastercommodities',
    localField: 'commodityId',
    pipeline: [
      {
        $match: filters.searchByCommodity ? {
          name: { $regex: `${filters.searchByCommodity}`, $options: 'i' }
        } : {}
      },
      { $project: { name: 1 } }
    ],
    foreignField: '_id',
    as: 'commodityData',
  },
}

之后可以去掉第二个$match中对应的条件,且如果lookup返回空数组,后续$unwind会自动过滤掉这些文档,减少无效数据流转。

3. 优化索引策略

  • 创建复合索引加速初始查询:针对第一个$match的过滤条件,创建复合索引:
    db.qcinspections.createIndex({
      isSLCMQcInspection: 1,
      isDeleted: 1,
      businessUnitId: 1,
      createdAt: 1,
      status: 1
    })
    
    这个索引能覆盖初始查询的所有过滤条件,让$cursor阶段更快返回结果。
  • 确认关联集合的索引:确保mastercommodities._id、commodityvariants._id、businessunits._id、users._id这些关联字段都有默认的主键索引(MongoDB默认会给_id建索引,但需确认未被删除);对于businessunits内部lookup的businessId字段,创建索引db.businessunits.createIndex({businessId:1})加速嵌套lookup。
  • 优化模糊查询索引:如果CIDNumber、LOTNumber的模糊查询是前缀匹配(如搜索"ABC%"),可以创建普通索引并去掉$options: 'i'(或用$regex: '^ABC'),这样能利用索引;如果是任意模糊匹配,确保使用$text查询而非$regex,并完善text索引的配置。

4. 调整聚合阶段顺序

将$sort操作尽可能延后,或合并到$facet的records管道中:

{
  $facet: {
    records: [
      { $sort: { createdAt: filters.sortOrder === SortOrder.Descending ? -1 : 1 } },
      { $skip: (filters.pageNumber - 1) * filters.count },
      { $limit: filters.count }
    ],
    total: [{ $count: 'count' }],
  }
}

这样排序仅针对最终需要分页的数据集(如果前面过滤到位,数据集会更小),减少排序的内存和时间消耗。

5. 优化嵌套lookup

businessunits的lookup中嵌套了对businesses的查询,可考虑:

  • 在businessunits集合中预计算businessClientName字段,每次businesses的displayName更新时同步更新该字段,避免每次聚合都执行嵌套lookup;
  • 为businessunits创建包含_id和businessId的复合索引,同时为businesses创建_id和displayName的复合索引,加速嵌套查询的字段获取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 05:27:02