NodeJS - 内存不足:杀死大数据处理过程中的错误

时间:2014-01-17 21:07:32

标签: javascript node.js csv synchronization

我有几个.csv文件需要与另一个大型.csv文件(超过300,000行)进行比较,我在服务器上遇到Out of Memory错误。我在4GB RAM的服务器上运行这个,所以我不确定为什么会这样,但我的代码看起来像这样。 我正在使用ya-csv来读取csv行:

var csv = require('ya-csv');
var fs = require('graceful-fs');
var async = require('async');


var first_silo = [];
var second_Silo = [];
var combined = [];

var reader = csv.createCsvFileReader('december_raw.csv', {columnsFromHeader:true,'separator': ','});
var first = csv.createCsvFileReader('first_data.csv', {columnsFromHeader:false,'separator': ','});
var second = csv.createCsvFileReader('second_data.csv', {columnsFromHeader:false,'separator': ','})


async.series([
 //push data from other .csv files into arrays
function(callback){
   first.addListener('data', function(data){
      first_silo.push(data[0]);
   })
   first.addListener('end', function(){
      callback();
   })
},

function(callback){
   second.addListener('data', function(data){
       second_silo.push(data[0]);
   });
   second.addListener('end', function(data){
       callback();
   });
},

function(callback){
    reader.addListener('data', function(data){
       //compare the data from reader to each item in the first array and append the items that get a match to a .csv.
       for(var i=0;i<first_silo.length;i++){
           if(data[0] === first_silo[i]){
               fs.appendFileSync('results.csv', data[0]+","+first_silo[i])
               break;
           }
       } 
    });
},

function(callback){
    reader.addListener('data', function(data){
        //do the same with the first array as the second.
        for(var i=0;i<second_silo.length;i++){
            if(data[0] === second_silo[i]){
               fs.appendFileSync('results.csv', data[0]+","+second_silo[i]);
               break;
            }
        }
    })
}
])

当我这样做时,我没有得到过去的first_silo比较。节点应用程序将停止,我可以在我dmesg时看到内存不足错误。

我也试图用这个标志运行这个程序:

- 最大岁空间大小= 3000

我仍然得到同样的错误。

有更聪明的方法吗?任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

由于某些原因,您的算法运行效率非常低。请原谅我,但是如果不使用您正在使用的async.series电话,我将会这样做。希望它仍然有用。

首先是第一件事:我正在做出一个假设。我假设您的第一个文件december_raw.csv的数据大小小于您的第二个和第三个文件。即使不是这种情况,只要文件的内容没有超出内存限制,这仍然可以在没有内存不足的情况下工作。

其次,您正在同时加载两个数组,而不是一次加载一个数组。这基本上是你的内存使用量增加了一倍。

第三,我的预感是,当你运行csv.createCsvFileReader时,你会同时开始所有这些流的流。你可能不想要这个。

因为您要将两个文件与december_raw.csv的内容进行比较,所以最好将该文件的内容完全加载到内存中,然后使用a将其他两个文件串行比较。 callBack和通用比较函数。

var csv = require('ya-csv');
var fs = require('graceful-fs');

var reader_silo = []; // a variable that holds the rows of the main csv.

var reader = csv.createCsvFileReader('december_raw.csv', {columnsFromHeader:true,'separator': ','});
reader.addListener('data', function(data){
  reader_silo.push(data[0]); // load each read in row into the array
});

reader.addListener('end', function(){
  //start comparing with first csv file.
  compareRows('first_data.csv', function(){
    // compare with second data
    compareRows('second_data.csv');
  });
});

// the comparison function, takes in the filename, and a callBack if there is one.    
function compareRows(csvFileName, callBack){

  var csvStream = csv.createCsvFileReader(csvFileName, {columnsFromHeader:false,'separator': ','}); // begin stream

  csvStream.addListener('data', function(data){
    for (var i = 0; i < reader_silo.length; i++) {
      if(data[0] === reader_silo[i]){
        fs.appendFileSync('results.csv', data[0]+","+reader_silo[i]);
        break;
      }
    }
  });

  csvStream.addListener('end', function(data){
    // if there's a callBack then we can execute it.
    // in this case the first time it is executed there is a callBack which executes this function again with the next file.
    if(callBack && typeof callBack === "function") callBack();
  });
}

PS。如果您的脚本继续超出此范围,您可能还需要考虑在完成比较后将reader_silo清零。所以你的'end'监听器callBack看起来像这样:

reader.addListener('end', function(){
  compareRows('first_data.csv', function(){
    compareRows('second_data.csv', function(){
      reader_silo = [];
    });
  });
});

答案 1 :(得分:0)

这是一个更具记忆效率的答案,没有任何假设。 在其中,您确保将最小的CSV文件作为compareRows函数的第一个参数传递。

通过仅保留存储在内存中的最小集合,这确实可以确保您的内存效率尽可能高。

var csv = require('ya-csv');
var fs = require('graceful-fs');

var smallFileName = ""; // used to see if we need to really reload the file again.
var smaller_silo = [];

compareRows('smaller.csv', 'larger.csv', function(){
  compareRows('smaller.csv', 'anotherLarger.csv', function(){
    smaller_silo = []; }); // done
});

function compareRows(smallerFileName, largerFileName, callBack){

  var reader;
  if(smallerFileName !== smallFileName){
    smallFileName = smallerFileName;
    reader = csv.createCsvFileReader(smallerFileName, { columnsFromHeader: true, separator: ','});
    reader.addListener('data', function(data){
      smaller_silo.push(data[0]);
    });

    reader.addListener('end', function(){
      compareSmallerToLarger(largerFileName, callBack);
    });
  }
  else{
    compareSmallerToLarger(largerFileName, callBack);
  }
}

function compareSmallerToLarger(largerFileName, callBack){

  var csvStream = csv.createCsvFileReader( largerFileName, { columnsFromHeader: false, 'separator':','});
  csvStream.addListener('data', function(data){
    for (var i = 0; i < smaller_silo.length; i++) {
      if(data[0] === smaller_silo[i]){
        fs.appendFileSync('results.csv', data[0]+","+smaller_silo[i]);
        break;
      }
    }
  });
  csvStream.addListener('end', function(data){
    if(callBack && typeof callBack === "function") callBack();
  });
}

无论如何,我不应该痴迷于......