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

如何优化MongoDB查询以提升API响应速度?

问题分析与优化方案

核心问题点

  1. 不区分大小写的正则查询无法利用普通索引
    你使用了new RegExp(params.search, 'i')这类无前缀限制的不区分大小写正则,MongoDB的普通单字段索引无法被这类查询命中,会触发全集合扫描,这是慢查询的主要诱因。

  2. $or + $and的组合查询导致索引失效
    MongoDB对$or的索引支持要求每个分支都有对应索引,但当$or与$and组合、且$or包含大量字段时,查询优化器很难选择最优索引,大概率会放弃索引走全表扫描。

  3. skip分页的性能损耗
    skip()会让数据库先遍历跳过指定数量的文档,即便只有3000条数据,当offset较大时,这个操作的耗时也会明显增加。

  4. 重复查询的额外开销
    countDocuments()和find()是两次独立查询,相当于扫描了两次集合,额外增加了IO消耗。


具体优化步骤

1. 替换正则查询为文本索引查询

将需要模糊搜索的字段统一创建文本索引,用MongoDB原生文本查询替代正则,既能支持不区分大小写的模糊搜索,又能高效利用索引:

// 先在MongoDB中创建文本索引(仅需执行一次)
db.supplierProfiles.createIndex({
  refNo: "text",
  businessName: "text",
  tradingName: "text",
  "agentTypes.name": "text",
  "departments.name": "text",
  "locality.name": "text",
  "type.name": "text",
  "status.name": "text",
  "factories.name": "text",
  "externalReferences.source": "text",
  "externalReferences.id": "text",
  "classification.name": "text",
  "incoterms.name": "text",
  "addresses.country.name": "text"
})

// 代码中替换$or正则逻辑为文本查询
if (params.search) {
  searchQuery.$text = { $search: params.search };
}

文本查询默认支持不区分大小写,且匹配效率远高于无限制正则。

2. 优化过滤条件的索引配置

对于status.id、agentTypes.id这类精确匹配的嵌套字段,确保创建对应单字段索引:

db.supplierProfiles.createIndex({ "status.id": 1 })
db.supplierProfiles.createIndex({ "agentTypes.id": 1 })
// 其他过滤字段同理创建单字段索引

如果经常同时使用多个过滤条件,可创建复合索引提升组合查询效率,比如:

db.supplierProfiles.createIndex({ "status.id": 1, createdAt: -1 })

3. 替换skip分页为基于唯一键的分页

利用你排序用的refNo(需保证唯一)替代skip,避免遍历跳过大量文档:

// 假设前端传递上一页最后一条的refNo:lastRefNo
let query = {...searchQuery};
if (params.lastRefNo) {
  query.refNo = { $gt: params.lastRefNo }; // 对应排序规则refNo:1
}

// 查询时移除skip,直接用query过滤
profiles = await SModel.find(query)
  .limit(parseInt(this.queryLimit, 10))
  .lean()
  .sort({ refNo: 1 })

这种方式完全依赖索引,无论offset多大,性能都保持稳定。

4. 优化计数查询

如果不需要精确的总条数,可用estimatedDocumentCount()替代countDocuments(),它直接读取集合元数据,速度极快:

const count = Object.keys(searchQuery).length 
  ? await SModel.countDocuments(searchQuery).lean()
  : await SModel.estimatedDocumentCount().lean()

5. 只返回必要字段

在find()中指定需要的字段,减少数据传输和序列化开销:

profiles = await SModel.find(query, { refNo:1, businessName:1, tradingName:1 /* 其他业务需要的字段 */ })
  .limit(parseInt(this.queryLimit, 10))
  .lean()
  .sort({ refNo: 1 })

6. 验证索引命中情况

用explain()查看查询执行计划,确认索引是否被正确使用:

await SModel.find(searchQuery).explain("executionStats")

若executionStats.totalDocsExamined等于集合总数(3000),说明仍在走全表扫描,需要调整索引或查询逻辑。


额外建议

  • 若agentTypes是数组类型,确保针对数组元素的索引已正确创建,MongoDB会自动处理数组索引,但需确认查询逻辑匹配索引规则。
  • 定期在业务低峰期执行db.collection.reIndex()重建索引,清理索引碎片。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 19:25:21