You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 09:15:04