NodeJS+Express+MongoDB内存泄漏问题排查求助
内存泄漏问题诊断与修复方案
你的服务内存泄漏并非单纯堆内存不足导致,核心问题出在定时任务的并发查询堆积、旧数据引用未及时回收,以及MongoDB查询的资源占用上,以下是针对性修复方案:
1. 给定时任务加并发锁,避免重复查询
如果前一次MongoDB查询还未完成,下一个定时任务就启动,会导致内存中同时存在多份查询结果,快速耗尽内存。加锁可以避免这种情况:
import { tokenModel } from '../../dbmodels/models.js'; let data = null; let isFetching = false; // 并发锁 setInterval(async () => { if (isFetching) return; // 正在查询则跳过本次任务 isFetching = true; try { data = null; // 先清空旧数据,帮助GC回收 data = await tokenModel.find({ $or: [ { "currentRanks.minuteTokenRank": {$lt: 51} }, { "currentRanks.fiveMinuteTokenRank": {$lt: 51} }, { "currentRanks.fifteenMinuteTokenRank": {$lt: 51} }, { "currentRanks.thirtyMinuteTokenRank": {$lt: 51} }, { "currentRanks.hourlyTokenRank": {$lt: 51} }, { "currentRanks.dailyTokenRank": {$lt: 51} }, { "currentRanks.weeklyTokenRank": {$lt: 51} } ] }).lean(); } catch (error) { console.error(error); } finally { isFetching = false; // 无论成败都释放锁 } }, 45000);
2. 优化MongoDB查询,减少内存占用
当前查询返回整个文档,若数据量大会持续占用内存。用select()只返回业务需要的字段,同时给查询字段加索引提升效率:
代码修改:
// 查询时只返回需要的字段,替换成你实际用到的字段 data = await tokenModel.find({/* 原查询条件 */}) .lean() .select('currentRanks name id'); // 示例字段,按需调整
给MongoDB加索引:
在你的Model定义中添加索引,加快查询速度,减少查询过程中的内存消耗:
// 在tokenModel的Schema定义中添加 tokenSchema.index({ "currentRanks.minuteTokenRank": 1 }); tokenSchema.index({ "currentRanks.fiveMinuteTokenRank": 1 }); // 其他rank字段按需添加单字段索引,或建立复合索引
3. 修复接口代码的冗余逻辑
原接口中dataRes = data;后设置dataRes = null完全无效,因为dataRes是局部变量,不会影响顶层data的引用,直接简化接口逻辑:
export async function main(req, res) { try { if (!data) { return res.status(503).json({ message: '数据加载中,请稍后再试' }); } res.status(200).json(data); } catch (err) { res.status(500).json({ message: err.message }); console.error('接口报错:', err.message); } }
4. 优化Mongoose连接配置
在server.js中给Mongoose连接添加连接池限制,避免过多连接占用内存:
// 修改连接代码 connect(process.env.DB_URL, { poolSize: 10, // 限制连接池大小 maxPoolSize: 10, connectTimeoutMS: 30000, });
5. 添加内存监控,确认修复效果
定时打印内存使用情况,验证泄漏是否解决:
// 在apis.js或server.js中添加 setInterval(() => { const { heapUsed } = process.memoryUsage(); console.log(`当前堆内存占用: ${(heapUsed / 1024 / 1024).toFixed(2)} MB`); }, 60000); // 每分钟打印一次
通过以上调整,应该能解决内存泄漏问题。核心是避免并发查询堆积,减少不必要的数据加载,帮助垃圾回收机制及时释放内存。
内容的提问来源于stack exchange,提问作者plat_winz
相关产品推荐
相关产品推荐

