优化MongoDB聚合查询:筛选近60天下单的客户
MongoDB聚合查询优化:筛选近60天有订单的客户
现有查询的问题分析
你的两个聚合查询效率低下的核心原因是从数据量更大的Customers集合出发做全量关联,且未合理利用索引:
- Aggregation 1:先对100万条客户数据全量关联Orders,再过滤时间符合条件的记录,相当于先完成100万次关联操作,再筛掉大部分结果,完全浪费算力。
- Aggregation 2:虽然在lookup子管道中加入了时间过滤,但错误地将
$project放在$match之前,导致MongoDB无法利用Orders集合的created_at索引(索引需在字段未被裁剪前使用),依然要处理大量无关订单数据。
优化思路:从Orders集合反向关联
Orders集合数据量(16万)远小于Customers(100万),先筛选出近60天的订单,再关联对应客户,能大幅减少关联的数据量。同时调整索引和聚合步骤,最大化利用索引效率。
具体优化方案
1. 优化索引
给Orders集合创建复合索引,让时间筛选和订单ID查询一步完成,无需回表:
db.orders.createIndex({ created_at: 1, id: 1 })
该索引会让第一步的时间过滤直接通过索引完成,同时直接返回订单ID,不需要读取完整的订单文档。
2. 优化后的聚合查询
[ // 第一步:快速筛选近60天的订单,利用复合索引直接获取订单ID { $match: { created_at: { $gt: { $dateSubtract: { startDate: "$$NOW", unit: "day", amount: 60 } } } } }, // 第二步:只保留订单ID,减少数据传输量 { $project: { _id: 0, order_id: "$id" } }, // 第三步:关联对应的客户数据,利用Customers的order_id索引 { $lookup: { from: "customers", localField: "order_id", foreignField: "order_id", as: "customer_info" } }, // 展开客户数组(一个订单对应一个客户) { $unwind: "$customer_info" }, // 第四步:投影需要的客户字段(按需调整) { $project: { _id: "$customer_info._id", customer_id: "$customer_info.id", name: "$customer_info.name", phone: "$customer_info.phone", latest_order_time: "$created_at" } }, // 可选:去重,保留每个客户的最新订单记录(如果一个客户有多条近60天订单) { $group: { _id: "$_id", customer_id: { $first: "$customer_id" }, name: { $first: "$name" }, phone: { $first: "$phone" }, latest_order_time: { $max: "$latest_order_time" } } } ]
优化点说明
- 反向聚合顺序:从Orders出发先过滤,只处理近60天的订单(假设占比远低于100%),再关联客户,避免全量客户关联的巨大开销。
- 索引最大化利用:复合索引让时间筛选和ID获取一步完成,Customers的order_id索引在关联时直接命中。
- 数据裁剪前置:在聚合早期就用
$project裁剪不必要的字段,减少内存占用和数据传输。 - 动态日期计算:用
$dateSubtract结合$$NOW自动计算近60天的日期,无需手动修改查询条件。
内容的提问来源于stack exchange,提问作者tomas jindal
相关产品推荐
相关产品推荐

