节点使用mongoose插入大数据

时间:2014-09-01 03:43:10

标签: javascript node.js mongodb

我正在尝试使用mongoose将大型数据集插入mongodb。但在此之前,我需要确保我的for循环正常工作。

// basic schema settings
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TempcolSchema = new Schema({
    cid: {
        type: Number,
        required: true
    },
    loc:[]
});
TempcolSchema.index({
  'loc': "sphere2d"
});


// we can easily see from the output that the forloop runs correctly
mongoose.connect('mongodb://localhost/mean-dev', function(err){
    for (var i = 0; i < 10000000; i++) {
        var c = i;
        console.log(c);
    }
});

输出为1,2,3,4,......等

现在我想在for循环中添加一个mongoose save语句。

mongoose.connect('mongodb://localhost/mean-dev', function(err){
    var Tempcol = mongoose.model('Tempcol', TempcolSchema);

    for (var i = 0; i < 10000000; i++) {
        var c = i;
        console.log(c);
        var lon = parseInt(c/100000);
        var lat = c%100000;
        new Tempcol({cid: Math.random(), loc: [lon, lat]}).save(function(err){});
    }
});

输出仍然是1,2,3,4,.....然而for循环在一段时间后停止并且说达到了最大堆栈并且存在某种内存问题。此外,当我检查集合时,我意识到根本没有插入数据点。

所以有人知道可能会发生什么吗?感谢。

1 个答案:

答案 0 :(得分:2)

这里的问题是您正在运行的循环不等待每个操作完成。所以实际上你只是排队了1000个.save()个请求并尝试同时运行它们。您无法在合理的限制范围内执行此操作,因此您会收到错误响应。

async模块在​​处理该迭代器的回调时有各种迭代方法,其中最简单的直接方法是whilst。 Mongoose还可以为您处理连接管理,而无需嵌入回调,因为模型具有连接感知功能:

var tempColSchema = new Schema({
    cid: {
        type: Number,
        required: true
    },
    loc:[]
});

var TempCol = mongoose.model( "TempCol", tempColSchema );

mongoose.connect( 'mongodb://localhost/mean-dev' );

var i = 0;
async.whilst(
    function() { return i < 10000000; },
    function(callback) {
        i++;
        var c = i;
        console.log(c);
        var lon = parseInt(c/100000);
        var lat = c%100000;
        new Tempcol({cid: Math.random(), loc: [lon, lat]}).save(function(err){
            callback(err);
        });            
    },
    function(err) {
       // When the loop is complete or on error
    }
);

不是最神奇的方式,它仍然是一次写入,你可以使用其他方法来管理&#34;并发操作,但这至少不会炸掉调用堆栈。

使用MongoDB 2.6及更高版本,您可以使用Bulk Operations API,以便在服务器上一次处理多个写入。所以这个过程是类似的,但这次你可以在一次写入和响应中一次发送1000个服务器,这要快得多:

var tempColSchema = new Schema({
    cid: {
        type: Number,
        required: true
    },
    loc:[]
});

var TempCol = mongoose.model( "TempCol", tempColSchema );

mongoose.connect( 'mongodb://localhost/mean-dev' );

mongoose.on("open",function(err,conn) {

    var i = 0;
    var bulk = TempCol.collection.initializeOrderedBulkOp();

    async.whilst(
      function() { return i < 10000000; },
      function(callback) {
        i++;
        var c = i;
        console.log(c);
        var lon = parseInt(c/100000);
        var lat = c%100000;

        bulk.insert({ "cid": Math.random(), "loc": [ lon, lat ] });

        if ( i % 1000 == 0 ) {
            bulk.execute(function(err,result) {
                bulk = TempCol.collection.initializeOrderedBulkOp();
                callback(err);
            });
        } else {
            process.nextTick(callback);
        }
      },
      function(err) {
        // When the loop is complete or on error

        // If you had a number not plainly divisible by 1000
        if ( i % 1000 != 0 )
            bulk.execute(function(err,result) {
                // possibly check for errors here
            });
      }
    );

});

这实际上是使用了尚未在mongoose中公开的本机驱动程序方法,因此需要额外注意确保连接可用。这是一个例子,但不是唯一的方法,但重点是猫鼬&#34;魔法&#34;对于连接不是建在这里所以你应该确定它已经建立。

您需要处理多个项目,但如果不是这样,您应该在最后一个块中调用bulk.execute()并显示,但这取决于响应模数的数量。

重点是不要将一堆操作增加到不合理的大小,并保持处理有限。此处的流控制允许在进入下一次迭代之前需要一些时间才能实际完成的操作。因此,无论是批量更新还是一些额外的并行排队,都是您想要获得最佳性能的。

如果您不希望写入错误致命,而是以不同的方式处理错误,那么还有.initializeUnorderedBulkOp()形式。主要参见有关批量API的官方文档以及如何解释给出的响应的响应。