NodeJS下优化实现用户关注/取消关注艺人功能的方案
优化Node.js艺人关注/取消关注路由方案
你的现有实现最大问题是每次操作都要拉取完整的艺人、用户文档(尤其是艺人的fans_list数组在粉丝量上万时会非常庞大),修改后再全量写回,取消关注时还要遍历数组找元素删除,数据量大时性能会急剧下降。直接用MongoDB的原子更新操作就能解决,这些操作在数据库端执行,不需要把大数组加载到应用层,效率提升明显。
优化思路
- 用MongoDB原子操作符替代手动数组修改:
- 关注:
$addToSet(避免重复关注)添加数组元素,$inc增减粉丝计数 - 取消关注:
$pull移除数组元素,$inc减少粉丝计数
- 关注:
- 用MongoDB事务保证用户和艺人操作的一致性(要么都成功,要么都失败)
- 仅检查用户/艺人是否存在,不加载完整文档,减少数据传输开销
优化后的代码实现
import { Request, Response } from 'express'; import { ArtistModel, UserModel } from '../models'; // 替换为你的模型路径 import { castToObjectId } from '../utils'; // 替换为你的工具函数路径 import mongoose from 'mongoose'; export const followArtist = async (req: Request, res: Response) => { const { artistId, userId, action } = req.body; const session = await mongoose.startSession(); try { session.startTransaction(); const artistObjectId = castToObjectId(artistId); const userObjectId = castToObjectId(userId); // 仅检查艺人、用户是否存在(不加载完整文档) const artistExists = await ArtistModel.exists({ _id: artistObjectId }).session(session); const userExists = await UserModel.exists({ _id: userObjectId }).session(session); if (!artistExists || !userExists) { await session.abortTransaction(); return res.status(404).send("艺人或用户不存在"); } let updateResult; // 处理关注操作 if (action === 'follow') { await ArtistModel.updateOne( { _id: artistObjectId }, { $addToSet: { fans_list: userObjectId }, $inc: { fans: 1 } }, { session } ); await UserModel.updateOne( { _id: userObjectId }, { $addToSet: { following: artistObjectId } }, { session } ); updateResult = "关注成功"; } // 处理取消关注操作 else if (action === 'unfollow') { await ArtistModel.updateOne( { _id: artistObjectId }, { $pull: { fans_list: userObjectId }, $inc: { fans: -1 } }, { session } ); await UserModel.updateOne( { _id: userObjectId }, { $pull: { following: artistObjectId } }, { session } ); updateResult = "取消关注成功"; } else { await session.abortTransaction(); return res.status(400).send("无效的操作类型"); } await session.commitTransaction(); return res.status(200).send(updateResult); } catch (error) { await session.abortTransaction(); console.error('操作失败:', error); return res.status(500).send("操作失败,请稍后重试"); } finally { session.endSession(); } };
关键优化点说明
- 原子操作:
$addToSet、$pull、$inc都是MongoDB原子操作,直接在数据库端修改数组和计数,不需要加载整个大文档到应用层,性能不受粉丝量大小影响。 - 事务一致性:用户关注列表和艺人粉丝列表、粉丝数是关联操作,事务确保两个更新要么都成功,要么都回滚,避免数据不一致。
- 高效存在性检查:用
exists()替代findById(),仅验证ID存在性,不加载完整文档,减少数据传输开销。 - 幂等性处理:
$addToSet自动忽略已存在元素,避免重复关注;$pull自动忽略不存在元素,避免重复取消关注报错。
额外建议
- 给
ArtistModel的fans_list和UserModel的following字段添加索引:{ fans_list: 1 }和{ following: 1 },进一步提升$addToSet和$pull的操作效率。 - 如果不需要实时查询艺人所有粉丝列表,可考虑将
fans_list单独存到ArtistFans集合,用文档分片优化大数据量的存储与查询。
内容的提问来源于stack exchange,提问作者user3118363
相关产品推荐
相关产品推荐

