如何优化MongoDB获取带最新消息的会话列表的聚合查询速度
优化方案
核心优化点
- 新增必要索引(性能提升最明显)
给chatmessages集合创建复合索引:db.chatmessages.createIndex({chat: 1, created_at: -1})
原理:子管道中先匹配chat字段、再按created_at倒序取第一条的逻辑,完全匹配该复合索引的排序规则,MongoDB匹配到对应会话的消息后,可直接按索引顺序返回结果,完全避免内存排序操作,解决排序耗时高的核心问题。
同时给chats集合的user和person字段分别创建单键索引,加快第一步会话匹配的速度:db.chats.createIndex({user: 1})db.chats.createIndex({person: 1}) - 调整聚合管道执行顺序,减少待处理文档量
将会话排序、limit操作提前到关联用户、关联消息之前,原逻辑是先处理所有符合条件的会话的关联操作,再取前20条,调整后仅需要处理20条会话的关联逻辑,大幅减少不必要的计算开销。 - 删除冗余的
$group阶段
原管道中的$group无实际作用,$lookup关联出来的messages本身就是长度为1的数组,直接提取第一个元素即可,省去分组的额外开销。
优化后代码示例
var pipeline = [ // 匹配当前用户的会话 {$match: { $or:[ {"user": ObjectId(req.query.user)}, {"person": ObjectId(req.query.user)}, ] }}, // 提前按会话创建时间倒序,取最近20条,减少后续处理的文档数 {"$sort": { created_at: -1 } }, {"$limit": 20}, // 关联创建者信息 {"$lookup": { "from": "users", "localField": "user", "foreignField": "_id", "as": "creator" }}, {"$unwind": "$creator"}, // 关联联系人信息 {"$lookup": { "from": "users", "localField": "person", "foreignField": "_id", "as": "contact" }}, {"$unwind": "$contact"}, // 关联最新消息,加索引后此处排序几乎无开销 {$lookup: { from: "chatmessages", let: { chatId: "$_id" }, pipeline: [ {$match: { $expr: { $eq: ["$chat", "$$chatId"] } }}, {$sort: {"created_at": -1}}, {"$limit": 1}, ], as: "messages" }}, // 提取字段,筛选存在消息的会话 {$project: { _id: 1, created_at:1, count:1, creator: 1, contact:1, lastMessage: { $arrayElemAt: ["$messages", 0] } }}, {$match: { lastMessage: {$exists: true} }} ]; var resultants = await Chat.aggregate(pipeline);
以上优化完成后,查询耗时可降至300ms以内,同时保证返回的是各会话最新的消息。
内容的提问来源于stack exchange,提问作者dream to code
相关产品推荐
相关产品推荐

