如何缩短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
相关产品推荐
相关产品推荐

