首页 > 解决方案 > NodeJS:使用管道从可读流写入文件会导致堆内存错误

问题描述

我正在尝试创建 1.5 亿行数据并将数据写入 csv 文件,以便我可以将数据插入到不同的数据库中而无需进行任何修改。

我正在使用一些函数来生成看似随机的数据并将数据推送到可写流中。

我现在拥有的代码无法成功处理内存问题。

经过几个小时的研究,我开始认为我不应该在 for 循环结束时推送每个数据,因为管道方法似乎根本无法以这种方式处理垃圾收集。

此外,我还发现了一些 StackOverFlow 答案和 NodeJS 文档,它们完全不推荐使用 push。

但是,我对 NodeJS 很陌生,我觉得我被阻止了,不知道如何从这里开始。

如果有人可以为我提供有关如何进行的任何指导并给我一个例子,我将非常感激。

下面是我的代码的一部分,可以让您更好地理解我想要实现的目标。

PS -

我找到了一种完全不使用管道方法来成功处理内存问题的方法——我使用了排水事件——但我必须从头开始,现在我很想知道是否有一种简单的方法来处理这个内存在不完全更改这段代码的情况下发出问题。

此外,我一直试图避免使用任何库,因为我觉得应该有一个相对简单的调整来使这项工作不使用库,但如果我错了,请告诉我。先感谢您。

// This is my target number of data
const targetDataNum = 150000000; 

// Create readable stream
const readableStream = new Stream.Readable({
  read() {}
});

// Create writable stream
const writableStream = fs.createWriteStream('./database/RDBMS/test.csv');

// Write columns first
writableStream.write('id, body, date, dp\n', 'utf8');

// Then, push a number of data to the readable stream (150M in this case)
for (var i = 1; i <= targetDataNum; i += 1) {
  const id = i;
  const body = lorem.paragraph(1);
  const date = randomDate(new Date(2014, 0, 1), new Date());
  const dp = randomNumber(1, 1000);
  const data = `${id},${body},${date},${dp}\n`;
  readableStream.push(data, 'utf8');
};

// Pipe readable stream to writeable stream
readableStream.pipe(writableStream);

// End the stream
readableStream.push(null);

标签: javascriptnode.jscsvheap-memorynodejs-stream

解决方案


我建议尝试如下解决方案:

const { Readable } = require('readable-stream');

class CustomReadable extends Readable {
  constructor(max, options = {}) {
    super(options);
    this.targetDataNum = max;
    this.i = 1;
  }

  _read(size) {
    if (i <= this.targetDataNum) {
      // your code to build the csv content
      this.push(data, 'utf8');
      return;
    }
    this.push(null);
  }
}

const rs = new CustomReadable(150000000);

rs.pipe(ws);

只需使用您的部分代码完成它以填充 csv 并创建可写流。

使用此解决方案,您将调用rs.push方法留给调用的内部_read流方法,直到this.push(null)不调用。可能在您填充内部流缓冲区太快之前,push在循环中手动调用会出现内存错误。


推荐阅读