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
相关产品推荐
相关产品推荐

