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

MongoDB find()查询1011条数据耗时超4分钟的优化咨询

MongoDB全量查询性能优化方案

问题背景

使用以下代码查询MongoDB数据:

const apprenticeshipstandards = await Apprenticeshipstandard.find();

返回1011条数据耗时超过4分钟,此前查询正常但突然变慢。该集合数据从外部API同步而来,自有库查询速度极慢。对应的Mongoose Schema如下:

const mongoose = require('mongoose');

const ApprenticeshipstandardsSchema = mongoose.Schema({
    templateType : { type : String },
    larsCode : { type : String },
    referenceNumber : { type : String, required : true },
    title : { type : String, required : true },
    status : { type : String, required : true },
    url : { type : String },
    versionNumber : { type : String },
    change : { type : String },
    changedDate : { type : String },
    earliestStartDate : { type : String },
    latestStartDate : { type : String },
    latestEndDate : { type : String },
    overviewOfRole : { type : String },
    level : { type : Number },
    typicalDuration : { type : Number },
    maxFunding : { type : Number },
    route : { type : String },
    keywords : { type : Array },
    jobRoles : { type : Array },
    entryRequirements : { type : String },
    assessmentPlanUrl : { type : String },
    ssa1 : { type : String },
    ssa2 : { type : String },
    version : { type : String },
    standardInformation : { type : String },
    occupationalSummary : { type : String },
    knowledges : { type : Array },
    behaviours : { type : Array },
    skills : { type : Array },
    options : { type : Array },
    optionsUnstructuredTemplate : { type : Array },
    proposalApproved : { type : String },
    standardApproved : { type : String },
    epaApprovalDate : { type : String },
    fundingApprovalDate : { type : String },
    eQAProvider : { type : Object },
    approvedForDelivery : { type : String },
    integratedApprenticeship : { type : Boolean },
    integratedDegree : { type : String },
    tbReference : { type : String },
    tbMainContact : { type : String },
    involvedEmployers : { type : String },
    regulated : { type : Boolean },
    regulatedBody : { type : String },
    coreAndOptions : { type : Boolean },
    typicalJobTitles : { type : Object },
    greenJobTitles : { type : Object },
    englishAndMathsQualifications : { type : String },
    reviewDetails : { type : String },
    pathway : { type : Object },
    cluster : { type : Object },
    createdDate : { type : String },
    lastUpdated : { type : String },
    occupationalStandardUrl : { type : String },
    standardPageUrl : { type : String },
    qualifications : { type : Array },
    professionalRecognition : { type : Array },
    duties : { type : Array },
    regulationDetail : { type : Array },
    optionsUnstructuredKsbMapping : { type : Array },
    careerStarter : { type : Boolean },
});

module.exports = mongoose.model('Apprenticeshipstandards', ApprenticeshipstandardsSchema);

查询执行计划如下:

{"explainVersion":"1","queryPlanner":{"namespace":"RubitekDB.apprenticeshipstandards","indexFilterSet":false,"parsedQuery":{},"maxIndexedOrSolutionsReached":false,"maxIndexedAndSolutionsReached":false,"maxScansToExplodeReached":false,"winningPlan":{"stage":"COLLSCAN","direction":"forward"},"rejectedPlans":[]},"executionStats":{"executionSuccess":true,"nReturned":1011,"executionTimeMillis":13,"totalKeysExamined":0,"totalDocsExamined":1011,"executionStages":{"stage":"COLLSCAN","nReturned":1011,"executionTimeMillisEstimate":12,"works":1013,"advanced":1011,"needTime":1,"needYield":0,"saveState":1,"restoreState":1,"isEOF":1,"direction":"forward","docsExamined":1011},"allPlansExecution":[]},"command":{"find":"apprenticeshipstandards","filter":{},"skip":0,"limit":0,"maxTimeMS":60000,"$db":"RubitekDB"},"serverInfo":{"host":"ac-hwcjvc4-shard-00-02.rwi1fym.mongodb.net","port":27017,"version":"5.0.13","gitVersion":"cfb7690563a3144d3d1175b3a20c2ec81b662a8f"},"serverParameters":{"internalQueryFacetBufferSizeBytes":104857600,"internalQueryFacetMaxOutputDocSizeBytes":104857600,"internalLookupStageIntermediateDocumentMaxSizeBytes":16793600,"internalDocumentSourceGroupMaxMemoryBytes":104857600,"internalQueryMaxBlockingSortMemoryUsageBytes":33554432,"internalQueryProhibitBlockingMergeOnMongoS":0,"internalQueryMaxAddToSetBytes":104857600,"internalDocumentSourceSetWindowFieldsMaxMemoryBytes":104857600},"ok":1,"$clusterTime":{"clusterTime":{"$timestamp":"7158726415230173191"},"signature":{"hash":"g2I1o5BLMSdftjXrP0sffIDyIM8=","keyId":{"low":11,"high":1655126098,"unsigned":false}}},"operationTime":{"$timestamp":"7158726415230173191"}}

执行计划分析

从执行计划可以看到,数据库端查询仅耗时13ms,说明慢查询的问题并非出在MongoDB的查询执行阶段,而是在数据传输、Mongoose的文档处理环节,或是网络/集群环境层面。

优化方案

1. 投影查询,只获取需要的字段

你的Schema包含大量字段(数组、长文本、嵌套对象),默认find()会返回所有字段,导致数据传输量巨大。明确指定需要的字段,能大幅减少传输和处理的数据量:

// 示例:只查询业务必需的字段
const apprenticeshipstandards = await Apprenticeshipstandard.find({}, 'referenceNumber title status level route');

如果需要排除少数字段,也可以用-前缀:

// 排除大文本字段
const apprenticeshipstandards = await Apprenticeshipstandard.find({}, '-overviewOfRole -standardInformation');

2. 使用lean()跳过Mongoose文档实例化

Mongoose默认会把查询结果转换为带有方法的文档对象,这会带来额外的性能开销。使用lean()可以直接返回原生JavaScript对象,提升处理速度:

const apprenticeshipstandards = await Apprenticeshipstandard.find().lean();

3. 分页加载,减少单次数据传输

如果业务允许,不要一次性加载所有1011条数据,改为分页查询:

// 示例:每页加载50条,第一页
const page = 1;
const pageSize = 50;
const apprenticeshipstandards = await Apprenticeshipstandard.find()
  .skip((page - 1) * pageSize)
  .limit(pageSize);

4. 缓存全量查询结果

如果数据更新频率不高,可以用Redis等缓存工具缓存全量查询结果,定时从数据库同步更新,避免每次都查询数据库:

// 伪代码示例
const cacheKey = 'apprenticeship_standards_all';
let data = await redis.get(cacheKey);
if (!data) {
  data = await Apprenticeshipstandard.find().lean();
  await redis.set(cacheKey, JSON.stringify(data), 'EX', 3600); // 缓存1小时
} else {
  data = JSON.parse(data);
}

5. 检查MongoDB集群状态

你使用的是MongoDB Atlas分片集群,需要检查:

  • 集群节点的CPU、内存、网络IO负载(通过Atlas控制台查看监控)
  • 客户端与集群之间的网络延迟,是否存在跨区域访问的情况
  • 分片集群的数据分布是否均衡,是否存在热点分片

6. 检查数据同步过程

数据从外部API同步时,可能出现文档结构异常、大字段冗余等情况:

  • 查看集合中文档的平均大小,是否存在异常大的文档
  • 验证同步逻辑是否正确,是否重复插入或更新了大量冗余数据
  • 尝试重新同步一次数据,排除数据损坏的可能

7. 索引优化(针对后续可能的过滤查询)

当前是全量查询,索引无法优化COLLSCAN,但如果后续查询需要添加过滤条件,可以提前在常用过滤字段(如status、level、route)上创建索引:

// 在Mongoose Schema中定义索引
const ApprenticeshipstandardsSchema = mongoose.Schema({
  // ... 其他字段
  status: { type: String, required: true, index: true },
  level: { type: Number, index: true }
});
// 或者手动创建索引
await Apprenticeshipstandard.createIndex({ status: 1 });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 10:00:55