Nodejs中mongoose多次查询导致内存一直在涨

Nodejs中mongoose多次查询导致内存一直在涨

代码如下: function loadAll() { // load all data updateService[prop].load(load_data); var mem = process.memoryUsage(); console.log(‘rss:’, Math.round((mem.rss / 1024)) + “KB”); } setInterval(loadAll, 1000 * 0.5);

加载代码: TopadSchema = mongoose.Schema({ name : String, desc : String, updateTime : Number, }, { collection : ‘topad’ // 文档名 });

var TopadModel = mongoose.model('Topad', TopadSchema);

exports.loadTopad = function(callback) { TopadModel.find({}).sort(’-updateTime’).limit(1).exec( function(err, topads) { if (err) { if (callback) callback(err); return; } if (!topads || topads.length === 0) { if (callback) callback(null, {}); return; }

var topad_res = topads[0]; if (callback) callback(null, topad_res);

}); }

周期性执行的时候内存一直在涨, 貌似就是内存泄漏了。 请问各位大大,这个是什么情况呀。


3 回复

在Node.js中使用Mongoose进行数据库查询时,如果频繁地执行查询操作(如你提到的每半秒一次),可能会导致内存泄漏或内存占用不断增加。这通常是因为没有正确地管理数据库连接、查询结果或者回调函数中的资源释放。

可能的原因

  1. 查询结果未被正确处理:每次查询的结果都可能占用一定的内存空间,如果没有及时清理这些数据,内存使用量会逐渐增加。
  2. 数据库连接未关闭:如果数据库连接没有被正确关闭,可能会导致内存泄漏。
  3. 回调函数中的内存泄漏:如果回调函数中存在闭包或其他可能导致内存泄漏的情况,也会导致内存占用不断增加。

解决方案

1. 使用 lean() 方法

lean() 方法可以将Mongoose返回的文档转换为普通的JavaScript对象,这样可以减少内存占用。

exports.loadTopad = function(callback) {
    TopadModel.find({})
        .sort('-updateTime')
        .limit(1)
        .lean() // 将结果转换为普通对象
        .exec(function(err, topads) {
            if (err) {
                if (callback) callback(err);
                return;
            }
            if (!topads || topads.length === 0) {
                if (callback) callback(null, {});
                return;
            }
            var topad_res = topads[0];
            if (callback) callback(null, topad_res);
        });
};

2. 清理查询结果

确保在每次查询后,及时清理查询结果,避免不必要的内存占用。

function loadAll() {
    exports.loadTopad(function(err, topad) {
        if (err) {
            console.error('Error loading topad:', err);
            return;
        }

        // 处理查询结果
        updateService[prop].load(load_data);
        
        // 清理查询结果
        topad = null;

        var mem = process.memoryUsage();
        console.log(`rss: ${Math.round(mem.rss / 1024)} KB`);
    });

    // 如果需要周期性执行,可以设置一个定时器
    setTimeout(loadAll, 1000 * 0.5);
}

// 启动周期性加载
loadAll();

3. 关闭数据库连接

虽然在大多数情况下不需要手动关闭数据库连接,但如果你的应用程序在长时间运行过程中出现内存泄漏,可以考虑在适当的时间点关闭数据库连接。

process.on('SIGINT', function() {
    mongoose.connection.close(function () {
        console.log('Mongoose default connection disconnected through app termination');
        process.exit(0);
    });
});

通过上述方法,可以有效地减少内存泄漏的风险,并保持应用程序的稳定性和性能。


  1. 最好将console.log的日志贴出部分,以说明内存增涨的幅度。KB单位的内存变化对有垃圾回收机制的语言来说,正常的不能再正常。请将以MB为单位打印nodejs的内存变化。

  2. 请以--expose_gc来运行node,调用gc()后才打印内存的大小,以排除未回收的临时对象所造成的影响。例如:node --exporse_gc app.js,然后在loadall增加gc()

    function loadAll() {
    // load all data
    updateService[prop].load(load_data);
    gc();
    var mem = process.memoryUsage();
    console.log('rss:', Math.round((mem.rss / 1024/1024)) + "MB");
    }

从你提供的代码来看,问题可能出在 loadAll 函数周期性地调用 exports.loadTopad 方法,而每次调用都会进行一次数据库查询。如果数据量较大或者查询条件复杂,可能会导致内存占用持续增加。

可能的原因及解决方案

  1. 内存未释放

    • Mongoose 查询返回的结果对象可能会持有引用,导致垃圾回收器无法回收这部分内存。确保在使用完查询结果后,及时删除对这些对象的引用。
  2. 频繁的数据库连接

    • 如果连接池没有正确管理,频繁的数据库连接可能会导致内存泄露。可以尝试优化连接池配置。
  3. 缓存问题

    • 如果在多次查询中返回的结果没有变化,考虑引入缓存机制,避免重复查询相同的数据。

示例代码改进

你可以尝试以下改进:

let cachedTopad = null;  // 缓存最近查询到的数据

function loadAll() {
    updateService[prop].load(load_data);
    exports.loadTopad((err, topad) => {
        if (!err && topad) {
            cachedTopad = topad;  // 更新缓存
        }
        var mem = process.memoryUsage();
        console.log(`rss: ${Math.round((mem.rss / 1024))} KB`);
    });
}

setInterval(loadAll, 1000 * 0.5);

// 修改 loadTopad 方法以利用缓存
exports.loadTopad = function(callback) {
    if (cachedTopad) {
        // 如果已经有缓存,则直接返回缓存数据
        return callback(null, cachedTopad);
    }

    TopadModel.find({})
              .sort('-updateTime')
              .limit(1)
              .exec(function(err, topads) {
                  if (err) {
                      return callback(err);
                  }
                  if (!topads || topads.length === 0) {
                      return callback(null, {});
                  }
                  var topad_res = topads[0];
                  callback(null, topad_res);
              });
};

解释

  • 引入了一个 cachedTopad 变量来存储最近查询到的数据,这样可以在后续的查询中直接返回缓存中的数据,减少不必要的数据库查询。
  • 这种方式可以有效降低内存占用,因为减少了数据库查询和内存分配的次数。

通过这种方式,你可以有效地解决因频繁查询而导致的内存泄露问题。

回到顶部