MongoDB聚合查询有限投影下仍内存占用过高如何优化
问题根因
- 首先需要确认你创建的是完全覆盖查询逻辑的复合索引:覆盖索引要求同时包含
$match阶段的所有过滤字段、$project阶段的所有返回字段,正确索引结构应为{team_id: 1, template_id: 1, created_at: 1, _id: 1},如果索引缺少任意一个需要用到的字段,MongoDB就会触发回表,拉取完整文档到内存后再做投影。 - MongoDB 4.2之前的版本对聚合管道的覆盖索引支持不完善,即使索引符合覆盖条件,聚合优化器也不会自动跳过文档拉取阶段,仍会加载全量文档后再做字段提取。
- 执行计划中如果存在
FETCH阶段,就证明发生了回表,内存占用就是全量匹配文档的总大小,而非索引数据的大小。
优化方案
1. 优先使用普通find查询(效果最优)
如果你的查询逻辑只有过滤加返回指定字段,没有其他聚合计算需求,直接用find查询即可,find对覆盖索引的支持更成熟,符合条件时会自动跳过回表步骤:
db.formdatas.find( { "team_id": "<team_id>", "template_id": "<template_id>", "created_at": { "$gte": ISODate("2021-04-17T00:00:00.000Z"), "$lte": ISODate("2021-10-18T00:00:00.000Z") } }, { _id: 1 } )
2. 显式指定覆盖索引
如果必须使用聚合管道,先创建符合要求的复合索引:
db.formdatas.createIndex({team_id: 1, template_id: 1, created_at: 1, _id: 1})
再在聚合参数中添加hint强制使用该索引,触发覆盖扫描:
db.getCollection("formdatas").aggregate( [ { $match : { "team_id": "<team_id>", "template_id": "<template_id>", "created_at": { "$gte": ISODate("2021-04-17T00:00:00.000Z"), "$lte": ISODate("2021-10-18T00:00:00.000Z") } } }, { $project: { _id: 1 } } ], { hint: {team_id: 1, template_id: 1, created_at: 1, _id: 1} } )
3. 优化效果验证
执行查询时加上explain("executionStats")参数,查看返回结果中的executionStats.totalDocsExamined字段,如果值为0就证明没有回表拉取文档,已成功触发覆盖索引,内存占用会降低到仅为索引扫描的量级。
内容的提问来源于stack exchange,提问作者Sharath Chandra
相关产品推荐
相关产品推荐

