插入10000文档超出最大调用堆栈大小

时间:2014-05-15 08:23:11

标签: node.js mongodb

这是运行的代码,它返回Range Maximum调用堆栈大小超出错误。 //使用node.js

将10000个值插入mongodb
            var MongoClient = require('mongodb').MongoClient;
            var mongoServer = require('mongodb').Server;
            var serverOptions = {
                'auto_reconnect': true,
                'poolSize': 100
            };
            var i=0;
            var async =require('async');
            var mongoClient = new MongoClient(new mongoServer('localhost', 27017, serverOptions));
            var db = mongoClient.db('test');
            var collection = db.collection('new_file_test');

               mongoClient.open(function (err, mongoClient) 
                {

                   if(err){console.log(err)};
                   function start(i,call) 
                {
                    if(i<10000) {
                        call(start);
                    }
                }
                function pass(callback) 
                {
                    Insert(save);
                    i++;
                    callback(i,pass);
                }

                start(i,pass); 
                 });    
                function Insert(callback) {
                     console.log("Inserting" );
                     var doc={
                'trip_paramid':i,
                'tripid':'116', 
                'lattitude':'12.8929183',
                'longitude':'77.63627',
                'speed':'2',
                'heading':'0',
                'altitude':'80469',
                'address':'qwertyasdfgxcvbn',
                'engine_status':'Normal',
                'oil_pressure': '83.12',
                'water_temp': '28',
                'fuel_content':'0',
                'brake':'Normal',
                'creation_time':'2013-08-31 23:22:17',
                'brakelight_status':'Normal',
                'battery_status':'12.68',
                'event_code':'8',
                'dbinsert_time':'2013-08-31 23:24:59',
                'gsm_status':'-51',
                'cell_id':'45',
                'vehicle_id':'123456',
                'distance':'0'}
                        callback(doc);          
                }   

                function save(doc)  
                {
                    collection.insert(doc, function(err) 
                    {
                    if (err) 
                    { 
                        console.log('Error occured'); 
                    }
                    else
                        console.log("Saved");
                    });
                }

如果条件是插入1000行,它可以正常工作,只有在条件超过10000时才会抛出错误。

2 个答案:

答案 0 :(得分:1)

循环超过10000次并执行插入是一个坏主意。但您仍然可以使用async library来帮助您解决问题。我之前遇到过这种情况,我使用async.queue来解决这个问题。

<强> Async.js module 即可。

答案 1 :(得分:1)

问题来自你所做的recursive loop

function start(i, call) {
    if (i < 10000) {
        call(start);
    }
}

function pass(callback) {
    Insert(save);
    i++;
    callback(i, pass);
}

start(i, pass);

您应该将其更改为

for (var i = 0; i < 10000; i++) {
   Insert(save);
}

简化您的代码:

var i = 0;
function pass() {
    if (i < 10000) {
        Insert(save);
        pass(i);
    }
    i++;
}

pass();

问题来自你以递归方式调用此函数的部分,并且由于javascript没有尾递归消除,callstack不断增长。 V8(nodejs javascript引擎)有它的限制,callstack一旦达到最大定义的大小就会抛出错误。

您还可以查看以下问题以获取更多信息:

这就是修复Maximum call stack size exceeded错误。但10000看起来像一个巨大的数字。我刚刚运行它,我的机器花了大约3秒钟,用monk完成循环。使用mongo shell需要大约1秒钟。如果您正在运行服务器,则在循环运行时,您的应用程序无响应。

我建议改为批量插入,并使用node's setImmediate function安排在挂起的I / O事件(如处理新的Web请求)之后运行下一批:

function insert10000(i) {
    insert100();
    i++;
    if (i < 100) {
        setImmidiate(insert10000, i);
    }
}

function insert100() {
    for (var i = 0; i < 100; i++) {
        Insert(save);
    }
}

由于我们提出了批量插入调用的主题,所以collection.insert方法支持一组文档,而不是只插入一个文档。

所以当我们目前有类似的事情时:

collection.insert(doc1);
collection.insert(doc2);

可以改为:

collection.insert([doc1, doc2]);

实际上这更快。所以您可以将代码更改为:

function insert10000(i) {
    insert100(i);
    i++;
    if (i < 100) {
        setImmediate(insert10000, i);
    }
}

function insert100(i) {
    var docs = [];
    for (var l = i + 1000; i < l; i++) {
        docs.push({
            'trip_paramid':i,
            'tripid':'116', 
            'lattitude':'12.8929183',
            'longitude':'77.63627',
            'speed':'2',
            'heading':'0',
            'altitude':'80469',
            'address':'qwertyasdfgxcvbn',
            'engine_status':'Normal',
            'oil_pressure': '83.12',
            'water_temp': '28',
            'fuel_content':'0',
            'brake':'Normal',
            'creation_time':'2013-08-31 23:22:17',
            'brakelight_status':'Normal',
            'battery_status':'12.68',
            'event_code':'8',
            'dbinsert_time':'2013-08-31 23:24:59',
            'gsm_status':'-51',
            'cell_id':'45',
            'vehicle_id':'123456',
            'distance':'0'
        });
    }
    collection.insert(docs, function(err) {
        if (err) {
            console.log('Error occurred', err); 
        }
    });
}

我测量了它,它比原始情况快两倍。