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

NodeJS+Express中setInterval缓存MongoDB数据的内存泄漏问题及优化问询

NodeJS MongoDB 定时缓存优化(解决内存泄漏隐患)

原代码的几个坑

  • setInterval 搭配异步函数容易触发任务堆积:如果MongoDB查询耗时超过30秒,下一次定时器会准时触发,同时运行多个查询,额外占用内存和数据库连接。
  • 全局变量data管理不够严谨:虽然每次赋值新查询结果会让旧数据被GC回收,但如果查询过程中出现未妥善处理的异常,或者异步任务引用未及时释放,可能留下内存隐患。
  • 未处理缓存初始化状态:服务启动后的前30秒内,data为null,请求会直接返回空值,用户体验差。

优化思路

1. 用递归setTimeout替代setInterval

确保每次缓存更新完成后再安排下一次任务,彻底避免并发查询堆积,减少内存占用。

2. 封装缓存管理逻辑

把缓存变量和更新逻辑封装在一起,提升代码可维护性,同时明确缓存的初始化状态。

3. 完善初始化与异常处理

服务启动时立即加载第一次缓存,无需等待30秒;查询失败时保留旧缓存,避免服务返回无效数据。

优化后的代码示例

// 封装缓存管理
let cache = {
  data: null,
  isInitialized: false
};

// 递归更新缓存的函数
async function updateCache() {
  try {
    const newData = await collection.find({
      data: { $ne: 'old' },
      $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();
    
    // 替换缓存数据,旧数据会被GC自动回收
    cache.data = newData;
    cache.isInitialized = true;
  } catch (error) {
    console.error('缓存更新失败:', error);
    // 查询失败时保留旧缓存,避免服务异常
  } finally {
    // 无论成功失败,30秒后执行下一次更新
    setTimeout(updateCache, 30000);
  }
}

// 服务启动时立即加载第一次缓存
updateCache();

export async function main(req, reply) {
  try {
    // 处理缓存未初始化的情况
    if (!cache.isInitialized) {
      return reply.status(503).send({ message: '服务初始化中,请稍后再试' });
    }
    // 直接返回缓存数据,无需多余的变量复制(GC会自动处理引用)
    reply.status(200).send(cache.data);
  } catch (err) {
    reply.status(500).send({ message: err.message });
    console.error('请求处理失败:', err.message);
  }
}

内存泄漏预防说明

  • 每次更新缓存时直接替换cache.data的引用,旧查询结果不再被任何变量持有,NodeJS垃圾回收器会自动回收这部分内存。
  • 递归setTimeout确保同一时间只有一个缓存更新任务在运行,不会出现多个异步查询堆积的情况,降低内存和数据库连接的占用。
  • 移除原代码中不必要的变量复制(比如datares),减少内存中的临时引用,让GC更高效地工作。

额外建议

  • 如果缓存数据量较大,可考虑对数据进行分页或按需裁剪,进一步降低内存占用。
  • 用NodeJS的--inspect参数启动服务,配合Chrome DevTools的内存快照功能,定期排查内存使用情况,确认无异常泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:54:57