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

如何处理Node.js读取MySQL海量数据时的内存不足异常?

问题描述

每日需从MySQL数据库读取6000万条记录,基于计算更新日期列,但使用Node.js和Sequelize读取数据时抛出heap out of memory异常。原实现代码如下:

db.sequelize.models.transaction.schema("dpunit").findAll({
    order: [
        ['txndate', 'asc']
    ]
}).then(txnList => {    // txnList 包含6000万条记录

let valueUpdated = [];
for (let tranz of txnList) {
  tranz.expdate = new Date() // 更新值
  valueUpdated.push(tranz.dataValues)
}

db.sequelize.models.transaction.schema("dpunit").bulkCreate(valueUpdated, { updateOnDuplicate: ["expdate"] }).then(data => {
                res.send({ data: data.Length });
   }).catch(err => {
       res.send({ error: err});
  });

});
最优解决方案

1. 优先使用纯SQL直接更新(性能最优)

如果更新逻辑(比如设置expdate为当前日期)可通过SQL表达式实现,完全不需要把数据拉到应用层处理,这是效率最高、内存占用最低的方案,直接在数据库端完成所有操作。

示例代码:

// 直接执行UPDATE语句,无需读取任何数据到内存
db.sequelize.models.transaction.schema("dpunit")
  .update(
    { expdate: db.sequelize.fn('NOW') }, // 用MySQL的NOW()函数获取当前日期,可根据实际计算逻辑调整
    { where: {} } // 如需过滤记录,在此添加条件(如特定txndate范围)
  )
  .then(updatedCount => {
    res.send({ data: updatedCount });
  })
  .catch(err => {
    res.send({ error: err });
  });

优势:彻底避免内存溢出,数据库端处理速度远快于应用层批量操作,减少网络IO消耗。

2. 分批分页读取+批量更新(适合必须在应用层处理计算的场景)

若更新逻辑无法用SQL实现,必须在Node.js中处理,则需避免一次性加载所有数据,改为分页分批读取,每处理完一批就执行更新,随后释放内存。

实现思路:

  • 按txndate或主键进行分页,每次读取固定数量的记录(比如1万条)
  • 处理当前批次记录后立即执行批量更新
  • 完成后读取下一批,直至所有数据处理完毕

示例代码:

const batchSize = 10000; // 每批次处理1万条,可根据内存情况调整
let offset = 0;
let totalUpdated = 0;

async function processBatch() {
  try {
    // 分页读取当前批次数据,只加载需要的字段减少内存占用
    const txnList = await db.sequelize.models.transaction.schema("dpunit").findAll({
      order: [['txndate', 'asc']],
      limit: batchSize,
      offset: offset,
      attributes: ['id', 'txndate']
    });

    if (txnList.length === 0) {
      // 所有批次处理完成
      res.send({ data: totalUpdated });
      return;
    }

    // 处理当前批次记录
    const updatedRecords = txnList.map(tranz => ({
      id: tranz.id,
      expdate: new Date() // 替换为你的实际计算逻辑
    }));

    // 批量更新当前批次
    const result = await db.sequelize.models.transaction.schema("dpunit")
      .bulkCreate(updatedRecords, { updateOnDuplicate: ["expdate"] });
    
    totalUpdated += result.length;
    offset += batchSize;

    // 递归处理下一批
    processBatch();
  } catch (err) {
    res.send({ error: err, processed: totalUpdated });
  }
}

// 启动分批处理
processBatch();

注意事项:

  • 仅读取必要字段(通过attributes指定),避免加载冗余数据占用内存
  • 灵活调整batchSize:若仍出现内存溢出则调小,内存充足时可适当调大提升效率
  • 可添加日志跟踪每批次处理进度

3. 使用Sequelize的流式查询(适合超大数据集逐行处理)

Sequelize支持流式查询,通过stream: true选项将数据以流的形式逐行读取,而非一次性加载到内存,适合处理超大规模数据。

示例代码:

let batch = [];
const batchSize = 5000;
let totalUpdated = 0;

// 创建查询流
const stream = db.sequelize.models.transaction.schema("dpunit").findAll({
  order: [['txndate', 'asc']],
  stream: true,
  attributes: ['id', 'txndate']
}).stream();

stream.on('data', async (tranz) => {
  // 暂停流,防止数据堆积
  stream.pause();

  // 处理当前记录
  batch.push({
    id: tranz.id,
    expdate: new Date() // 替换为你的实际计算逻辑
  });

  // 批次达到指定大小,执行批量更新
  if (batch.length >= batchSize) {
    try {
      const result = await db.sequelize.models.transaction.schema("dpunit")
        .bulkCreate(batch, { updateOnDuplicate: ["expdate"] });
      totalUpdated += result.length;
      batch = []; // 清空批次释放内存
    } catch (err) {
      stream.destroy(err); // 出错时终止流
      res.send({ error: err, processed: totalUpdated });
      return;
    }
  }

  // 恢复流
  stream.resume();
});

stream.on('end', async () => {
  // 处理剩余不足一个批次的记录
  if (batch.length > 0) {
    const result = await db.sequelize.models.transaction.schema("dpunit")
      .bulkCreate(batch, { updateOnDuplicate: ["expdate"] });
    totalUpdated += result.length;
  }
  res.send({ data: totalUpdated });
});

stream.on('error', (err) => {
  res.send({ error: err, processed: totalUpdated });
});

优势:内存占用极低,每次仅处理少量记录,不会一次性加载所有数据。

4. 临时调整Node.js内存限制(不推荐作为长期方案)

若以上方案无法立即实施,可临时调整Node.js堆内存大小,启动时添加--max-old-space-size参数:

node --max-old-space-size=8192 your-script.js # 分配8GB堆内存,根据服务器配置调整

缺点:治标不治本,6000万条记录即使分配更大内存,仍可能导致内存占用过高影响服务稳定性,仅适合临时应急。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 22:46:02