Node.js从MongoDB取1000条记录响应慢,求优化方案
兄弟,25秒才拿到1000条数据确实有点拉胯,我结合你的代码片段和MongoDB的性能优化经验,给你梳理几个立竿见影的优化方向:
1. 先搞定索引——性能提升的核心
你的查询条件是removed: false,排序用的是_id desc,这种场景下复合索引是必须的,不然MongoDB会做全表扫描+内存排序,慢到离谱。
给你的集合建个复合索引:
// 在MongoDB shell里执行 db.yourCollectionName.createIndex({ removed: 1, _id: -1 })
建完之后,用explain("executionStats")检查查询是否用到了这个索引:
collections.findItems({ removed: false }, {}, { sort: [['_id', 'desc']], limit: 1000 }).explain("executionStats")
看executionStats.executionStages.inputStage.stage是不是IXSCAN,如果是COLLSCAN说明索引没生效,得排查下索引是否建对。
2. 用投影(Projection)减少数据传输
你代码里的filter是空对象,意味着MongoDB会返回文档的所有字段,很多字段可能你根本用不上,这会白白浪费网络带宽和MongoDB的序列化时间。
比如你只需要name、email、createdAt这几个字段,就把投影改成:
const projection = { name: 1, email: 1, createdAt: 1 }; // 1表示返回该字段,0表示不返回
然后把这个projection传到findItems里,这样返回的数据量会大幅减少,响应速度自然就上去了。
3. 别一次性拉太多数据——用游标分批处理
你代码里options.limit = 10000,如果是一次性拉1万条数据,MongoDB需要把所有数据加载到内存再返回,压力很大。哪怕是1000条,用游标分批获取也比一次性fetch all更高效,因为游标是流式返回的,不会占用太多内存。
优化后的代码可以改成异步游标处理:
exports.getAll = async function(filters, sort, skip = 0, limit = 1000, callback) { try { const query = { removed: false, ...filters }; // 只返回需要的字段 const projection = { name: 1, email: 1, createdAt: 1 }; const options = { sort: sort || [['_id', 'desc']], skip: skip, limit: limit, noCursorTimeout: true // 避免游标超时,适合批量处理 }; const cursor = collections.findItems(query, projection, options); const results = []; // 分批遍历游标 await cursor.forEach(doc => { results.push(doc); }); // 手动关闭游标 await cursor.close(); callback(null, results); } catch (err) { callback(err); } };
4. 替换skip/limit的分页方式——避免深分页性能问题
如果你的分页是用skip + limit,当skip很大的时候(比如第100页,skip=99000),MongoDB需要先跳过前面99000条数据,性能会急剧下降。
建议改用基于游标的分页:比如记录上一页最后一条数据的_id,下一页查询时用_id: { $lt: lastId }来过滤,再配合limit,这样不需要skip,性能会好很多:
// 下一页查询示例 const query = { removed: false, _id: { $lt: lastPageLastId } // lastPageLastId是上一页最后一条的_id }; const options = { sort: [['_id', 'desc']], limit: 1000 };
5. 检查MongoDB的基础配置和服务器资源
- 确保MongoDB用的是WiredTiger引擎(默认是这个),它的缓存和压缩机制比旧引擎好很多;
- 检查服务器的内存:MongoDB会把热数据放到内存里,如果内存不够,会频繁读写磁盘,速度肯定慢;
- 看看磁盘IO是不是瓶颈:如果是机械硬盘,换成SSD会有质的飞跃;
- 开启慢查询日志,定位具体耗时的查询:在MongoDB配置里设置
slowms: 100,超过100ms的查询会被记录下来,方便排查。
6. 代码细节的小优化
- 你代码里
req.skip = 0看起来是笔误,应该用传入的skip参数,不然永远从第0条开始查; - 确保你的MongoDB驱动是最新版本,旧版本可能存在性能bug;
- 尽量用异步/await代替回调,代码更清晰,也更容易处理错误。
内容的提问来源于stack exchange,提问作者Bhrungarajni

