$lookup右表foreignField未建索引会导致全表扫描,10万文档单次关联超2秒,建索引后可降至毫秒级;需用getIndexes()确认key匹配,createIndex({“field”:1})创建单字段升序索引,嵌套字段须写完整路径,分片集群需各分片同步执行;explain验证时totalDocsExamined应接近nReturned,且localField与foreignField类型必须严格一致。

在MongoDB聚合管道中使用$lookup进行连表查询时,右表foreignField未建索引会导致每次匹配都全表扫描,10万文档的集合单次关联可能耗时超2秒,而正确建索引后可压至毫秒级。
确认foreignField是否已建索引
进入MongoDB Shell,执行db.otherCollection.getIndexes(),检查输出中是否存在key为{"foreignFieldName": 1}的索引项;若无,说明索引缺失,必须立即补建。
注意:不要只看索引名或创建时间,重点核对key字段是否完全匹配foreignField名称和方向——哪怕多一个空格或大小写错误,索引就无效。
为foreignField创建单字段升序索引
方法一:直接建索引
执行db.otherCollection.createIndex({ "foreignFieldName": 1 }),升序即可,$lookup等值匹配不依赖排序方向。
方法二:嵌套字段需完整路径
若foreignField是metadata.refId,索引必须写成db.otherCollection.createIndex({ "metadata.refId": 1 }),不能简写为refId或metadata。
【分片集群下必须同步在所有分片上执行该命令】,否则部分分片仍会全表扫描。
验证索引是否真正生效
第一步:用explain获取执行统计
执行db.leftCollection.aggregate([ { $lookup: { from: "otherCollection", localField: "localField", foreignField: "foreignFieldName", as: "joined" } } ]).explain("executionStats")
第二步:定位$lookup阶段的扫描数据量
在返回结果中找到stages[0].$lookup.executionStats节点,检查totalDocsExamined与nReturned是否接近;若前者远大于后者(例如10000 vs 12),说明索引未命中。
第三步:确认字段类型严格一致
分别运行typeof db.leftCollection.findOne().localField和typeof db.otherCollection.findOne().foreignFieldName,确保两者均为object(ObjectId)或均为string;类型不一致时索引完全失效且不报错。











