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

如何缩短MongoDB查询用户通知消息的耗时?技术求助

消息查询接口性能优化建议

用户登录时获取并返回用户消息,前端筛选unread消息显示红色提醒,当前请求耗时13秒。延迟源于API中循环查询联系人消息的逻辑,不确定创建索引能否提速,相关代码如下:

const getMessagesByUserId = async (req, res, next) => {
  const userId = req.params.userId;
  const authUserId = req.userData.userId;

  //verify the query supplied user id against the decrypted user id.
  if (userId !== authUserId) {
    return next(new HttpError("Unauthorized access, please try again", 401));
  }

  let contactsMessages = [];

  try {
    //queries for users contacts
    const usersContacts = await User.findById(userId).populate("contacts");

    //queries and returns contacts' messages
    const userContactsMessages = await Promise.all(
      usersContacts.contacts.map(async (contact) => {
        const messages = await Contact.findById(contact["_id"]).populate(
          "messages"
        );
        contactsMessages.push(...messages.messages)
      })
    );

  } catch (error) {
    console.log(error)
    return next(
      new HttpError("Fetching messages failed, please try again.", 500)
    );
  }

  //checks if contacts message array is empty
  if (!contactsMessages || contactsMessages.length === 0) {
    res.json({ messages: [] }).status(200).end();
    return;
  }

  res.json(
   contactsMessages.map((message) =>
      message.toObject({ getters: true })
    ),
  );
};

优化方案

1. 添加数据库索引提速查询

索引能直接降低数据库的查询耗时,针对当前逻辑需添加以下索引:

  • 给User集合的contacts字段加索引,加速联系人列表查询:
    User.collection.createIndex({ contacts: 1 });
    
  • 给Contact集合的messages字段加索引,加速消息关联查询:
    Contact.collection.createIndex({ messages: 1 });
    

如果Message集合中有标识消息状态(如read)的字段,也可以给该字段加索引,方便后续筛选未读消息。

2. 批量查询替代循环请求

原代码通过遍历每个联系人单独发起查询,会产生大量数据库IO请求。改成批量查询所有联系人的消息,大幅减少请求次数:

const getMessagesByUserId = async (req, res, next) => {
  const userId = req.params.userId;
  const authUserId = req.userData.userId;

  if (userId !== authUserId) {
    return next(new HttpError("Unauthorized access, please try again", 401));
  }

  try {
    // 仅获取用户的联系人ID列表,减少数据返回量
    const user = await User.findById(userId, "contacts");
    const contactIds = user.contacts;

    if (!contactIds || contactIds.length === 0) {
      return res.status(200).json({ messages: [] });
    }

    // 批量查询所有联系人,并一次性关联消息
    const contacts = await Contact.find({ _id: { $in: contactIds } }).populate("messages");

    // 合并所有联系人的消息
    const contactsMessages = contacts.flatMap(contact => contact.messages);

    // 返回转换后的消息数据
    res.json(contactsMessages.map(message => message.toObject({ getters: true })));
  } catch (error) {
    console.log(error);
    return next(new HttpError("Fetching messages failed, please try again.", 500));
  }
};

3. 只查询必要字段

避免查询无关字段,减少数据传输和处理时间:

  • 查询用户时仅取contacts字段:User.findById(userId, "contacts")
  • 查询联系人时仅取messages字段:Contact.find({ _id: { $in: contactIds } }, "messages")

4. 后端直接返回未读消息

原逻辑是后端返回所有消息,前端筛选未读。改成后端直接过滤未读消息,减少数据传输量:
在populate时添加匹配条件(假设Message集合有read字段标识已读状态):

const contacts = await Contact.find({ _id: { $in: contactIds } })
  .populate({
    path: "messages",
    match: { read: false } // 仅返回未读消息
  });

内容的提问来源于stack exchange,提问作者joshua crowe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:55:38