Javascript 使用mongoose将非常大的CSV保存到mongoDB
我有一个CSV文件,包含超过200000行。我需要把它保存到MongoDB 如果我尝试for循环,节点将耗尽内存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
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周期的情况下以内存高效的方式执行此操作的概念。公认的答案非常好,并试图涵盖此问题的所有重要方面
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
});
}
});
});