MongoDB查询百万级Chats集合慢且CPU占满,如何优化查询?
百万级MongoDB聊天记录查询优化方案
已做优化确认
你当前代码中已经给所有查询添加了.lean(),这个操作是正确的:.lean()会跳过Mongoose的文档实例化步骤,直接返回普通JavaScript对象,可降低30%左右的查询内存占用和耗时,这部分无需调整。
核心优化:添加索引(解决CPU占满、慢查询的关键)
你现在的查询耗时高的核心原因是没有添加匹配查询逻辑的索引,每次查询都会扫描全表100万条数据,CPU自然会被占满。按照你的查询逻辑,需要添加以下3个索引:
针对Chat集合的复合索引
你的查询逻辑是匹配两个用户的双向聊天记录,且按发送时间倒序排序,直接创建两个覆盖查询+排序的复合索引即可让查询完全走索引,无需回表:
// 在Chat的Schema定义中添加 ChatSchema.index({ sendBy: 1, sendTo: 1, createDateTimestamp: -1 }); ChatSchema.index({ sendTo: 1, sendBy: 1, createDateTimestamp: -1 });
针对User集合的唯一索引
你查询用户是按uuid字段匹配,给User集合的uuid字段加唯一索引,可让用户查询的耗时降低到毫秒级:
// 在User的Schema定义中添加 UserSchema.index({ uuid: 1 }, { unique: true });
代码层面补充优化
新增分页逻辑
你当前代码会一次性拉取两个用户的所有聊天记录,如果两人的聊天记录超过千条,响应速度和内存占用都会很高,建议加分页限制:
// 示例:每次查询20条记录,前端上拉加载更多时传入最后一条消息的时间戳即可查询历史记录 const pageSize = 20; const lastMsgTimestamp = req.query.lastMsgTimestamp || 0; Promise.all([ UserModel.findOne({ uuid: selfUuid }).lean(), UserModel.findOne({ uuid: chatWithUuid }).lean(), Chat.find({ $or: [ { sendBy: uuid, sendTo: chatWithUuid }, { sendBy: chatWithUuid, sendTo: uuid }, ], // 只查询比上次最后一条消息更早的记录 ...(lastMsgTimestamp ? { createDateTimestamp: { $lt: lastMsgTimestamp } } : {}) }) .lean() .sort({ createDateTimestamp: -1 }) .limit(pageSize) // 限制单次查询数量 ]) // 后续逻辑和原有一致
索引生效验证
本地测试时可给Chat查询添加.explain("executionStats"),查看输出结果中executionStats.totalDocsExamined的数值,如果和返回的消息条数一致,说明索引已经正常生效,没有全表扫描。
长期优化建议
- 冷热数据分离:将3个月以上的历史聊天记录归档到单独的冷数据集合,主集合只存放近期热数据,可进一步降低查询的数据集大小。
- 开启MongoDB慢查询日志,设置100ms阈值,定期排查未走索引的异常查询。
内容的提问来源于stack exchange,提问作者Fuangel Sikarin
相关产品推荐
相关产品推荐

