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

