Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/394.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使用mongoose将非常大的CSV保存到mongoDB_Javascript_Node.js_Mongodb_Mongoose - Fatal编程技术网

Javascript 使用mongoose将非常大的CSV保存到mongoDB

Javascript 使用mongoose将非常大的CSV保存到mongoDB,javascript,node.js,mongodb,mongoose,Javascript,Node.js,Mongodb,Mongoose,我有一个CSV文件,包含超过200000行。我需要把它保存到MongoDB 如果我尝试for循环,节点将耗尽内存 fs.readFile('data.txt', function(err, data) { if (err) throw err; data.split('\n'); for (var i = 0; i < data.length, i += 1) { var row = data[i].split(','); var obj = { /* T

我有一个CSV文件,包含超过200000行。我需要把它保存到MongoDB

如果我尝试for循环,节点将耗尽内存

fs.readFile('data.txt', function(err, data) {
  if (err) throw err;

  data.split('\n');

  for (var i = 0; i < data.length, i += 1) {
    var row = data[i].split(',');

    var obj = { /* The object to save */ }

    var entry = new Entry(obj);
    entry.save(function(err) {
      if (err) throw err;
    }
  } 
}
fs.readFile('data.txt',函数(err,data){
如果(错误)抛出错误;
data.split('\n');
对于(变量i=0;i

如何避免内存耗尽?

欢迎使用流媒体。您真正想要的是一个“事件流”,它“一次处理一个数据块”,当然最好是使用一个公共分隔符,例如您当前使用的“换行符”

对于真正高效的东西,您可以添加MongoDB插入的使用,以使加载尽可能快,而不会占用所有的机器内存或CPU周期

不提倡,因为有各种可用的解决方案,但这里有一个列表,它利用使“行终止符”部分变得简单

仅按“示例”列出的架构定义:

因此,通常那里的“流”接口“分解输入”,以便“一次处理一行”。这会阻止您一次加载所有内容

主要部分是来自MongoDB的。这允许您在实际发送到服务器之前一次“排队”许多操作。因此,在这种情况下,使用“模”,每处理1000个条目只发送一次写入。您可以真正执行任何高达16MB BSON限制的操作,但要保持可管理性

除了批量处理的操作外,库中还有一个额外的“限制器”。这并不是必需的,但这可以确保在任何时候处理的文档基本上不超过“模限”。一般的批量“插入”除了内存之外不需要IO成本,而是“执行”呼叫意味着IO正在处理。所以我们等待,而不是排队等待更多的东西


当然,对于“流处理”CSV类型的数据,您可以找到更好的解决方案。但总体而言,这为您提供了如何在不占用CPU周期的情况下以内存高效的方式执行此操作的概念。

公认的答案非常好,并试图涵盖此问题的所有重要方面

  • 将CSV文件作为行流读取
  • 将文档分批写入MongoDB
  • 读写同步
  • 虽然它在前两个方面做得很好,但使用async.series()解决同步问题所采用的方法不会像预期的那样工作

    stream.on("line",function(line) {
        async.series(
            [
                function(callback) {
                    var row = line.split(",");     // split the lines on delimiter
                    var obj = {};             
                    // other manipulation
    
                    bulk.insert(obj);  // Bulk is okay if you don't need schema
                                       // defaults. Or can just set them.
    
                    counter++;
    
                    if ( counter % 1000 == 0 ) {
                        bulk.execute(function(err,result) {
                            if (err) throw err;   // or do something
                            // possibly do something with result
                            bulk = Entry.collection.initializeOrderedBulkOp();
                            callback();
                        });
                    } else {
                        callback();
                    }
               }
           ],
           function (err) {
               // each iteration is done
           }
       );
    });
    
    这里bulk.execute()是一个mongodb写操作,它是一个异步IO调用。这允许node.js在bulk.execute()完成其db写操作和回调之前继续执行事件循环

    因此,它可能会继续从流中接收更多的“行”事件,并将更多文档排入队列
    bulk.insert(obj)
    ,然后再次点击下一个模来触发bulk.execute()

    让我们看看这个例子

    var async = require('async');
    
    var bulk = {
        execute: function(callback) {
            setTimeout(callback, 1000);
        }
    };
    
    async.series(
        [
           function (callback) {
               bulk.execute(function() {
                  console.log('completed bulk.execute');
                  callback(); 
               });
           },
        ], 
        function(err) {
    
        }
    );
    
    console.log("!!! proceeding to read more from stream");
    
    这是输出

    !!! proceeding to read more from stream
    completed bulk.execute
    

    为了真正确保在任何给定时间处理一批N个文档,我们需要使用
    stream.pause()
    &
    stream.resume()


    你一定要用mongoose来做这件事吗?Mongo有一个csv导入选项…@Alex问题是,我需要对结构和数据测量进行一些更改。好的,你可以尝试mongoose可写流-最后一条评论在你的问题中会很有用,而不是评论。仅供将来参考。可能会重复怎么做async.series确保在任何时候处理的文档都不超过“模限制”?我在这里看到的是对象在1000年内排队并写入数据库。如果在前一个bulk.execute()完成之前,下一个1000个对象准备就绪,它将触发另一个bulk.execute().我在这里遗漏了什么吗?@JayyVis显然是的,你遗漏了什么。正如我所说的“系列”有点做作,但实际执行有一个要点。使用模,你不能一次“排队”超过1000个操作。“系列”回调确保这是“内部”操作执行需要有完成。这要么是“批插入”,要么是实际的“执行”,有效地耗尽了队列。这通常是您希望避免内存消耗的模式。加上所有事件都会处理。@Jayyyvis不,您不需要。@async点是“保持”继续处理,直到完成为止。所以这可以在不同的级别处理。也许你应该先尝试代码,然后再发布“建设性的批评”事实上@JayKumar的回答是正确的。execute命令是异步的,在execute操作完成之前,可能会有更多行事件发生,最终会使用正在执行的同一个大容量对象。当执行完成时,以及在启动exe之前,是否有前一个大容量对象上不存在的任何insert操作当启动一个新的大容量对象时,所有这些文档都会丢失。这假设将文档插入正在执行的大容量对象不会引发任何异常。
    !!! proceeding to read more from stream
    completed bulk.execute
    
    var LineInputStream = require("line-input-stream"),
        fs = require("fs"),
        mongoose = require("mongoose"),
        Schema = mongoose.Schema;
    
    var entrySchema = new Schema({},{ strict: false });
    var Entry = mongoose.model( "Entry", entrySchema );
    
    var stream = LineInputStream(fs.createReadStream("data.txt",{ flags: "r" }));
    
    stream.setDelimiter("\n");
    
    mongoose.connection.on("open",function(err,conn) { 
    
        // lower level method, needs connection
        var bulk = Entry.collection.initializeOrderedBulkOp();
        var counter = 0;
    
        stream.on("error",function(err) {
            console.log(err); // or otherwise deal with it
        });
    
        stream.on("line",function(line) {
            var row = line.split(",");     // split the lines on delimiter
            var obj = {};             
            // other manipulation
    
            bulk.insert(obj);  // Bulk is okay if you don't need schema
                               // defaults. Or can just set them.
    
            counter++;
    
            if ( counter % 1000 === 0 ) {
                stream.pause(); //lets stop reading from file until we finish writing this batch to db
    
                bulk.execute(function(err,result) {
                    if (err) throw err;   // or do something
                    // possibly do something with result
                    bulk = Entry.collection.initializeOrderedBulkOp();
    
                    stream.resume(); //continue to read from file
                });
            }
        });
    
        stream.on("end",function() {
            if ( counter % 1000 != 0 ) {
                bulk.execute(function(err,result) {
                    if (err) throw err;   // or something
                    // maybe look at result
                });
            }
        });
    
    });