【问题标题】:How to populate mongoose with a large data set如何使用大型数据集填充猫鼬
【发布时间】:2013-01-21 03:06:52
【问题描述】:

我正在尝试使用 Node.js (0.8.18) 和 Mongoose (3.5.4) 将商店目录加载到 MongoDb (2.2.2) 中——所有这些都在 Windows 7 64 位上。该数据集包含大约 12,500 条记录。每个数据记录都是一个 JSON 字符串。

我最近的尝试是这样的:

var fs = require('fs');
var odir = process.cwd() + '/file_data/output_data/';
var mongoose = require('mongoose');
var Catalog = require('./models').Catalog;

var conn = mongoose.connect('mongodb://127.0.0.1:27017/sc_store');

exports.main = function(callback){
    var catalogArray = fs.readFileSync(odir + 'pc-out.json','utf8').split('\n');
    var i = 0;

    Catalog.remove({}, function(err){
        while(i < catalogArray.length){
            new Catalog(JSON.parse(catalogArray[i])).save(function(err, doc){
                if(err){
                    console.log(err);
                } else {
                    i++;                    
                }
            });
            if(i === catalogArray.length -1) return callback('database populated');
        }
    });
};

我在尝试填充数据库时遇到了很多问题。在以前的场景(和这个场景)下,节点与处理器挂钩并最终耗尽内存。请注意,在这种情况下,我试图让 Mongoose 保存一条记录,然后在记录保存后迭代到下一条记录。

但是 Mongoose 保存函数内部的迭代器永远不会增加。此外,它从不抛出任何错误。但是如果我将迭代器 (i) 放在对 Mongoose 的异步调用之外,它会起作用,前提是我尝试加载的记录数不是太大(我已经成功加载了 2,000 条这种方式)。

所以我的问题是:为什么 Mongoose 保存调用中的迭代器没有递增?而且,更重要的是,使用 Mongoose 将大型数据集加载到 MongoDb 的最佳方法是什么?

罗伯

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    i 是您从catalogArray 中提取输入数据的索引,但您也尝试使用它来跟踪已保存的数据数量,这是不可能的。尝试像这样单独跟踪它们:

    var i = 0;
    var saved = 0;
    Catalog.remove({}, function(err){
        while(i < catalogArray.length){
            new Catalog(JSON.parse(catalogArray[i])).save(function(err, doc){
                saved++;
                if(err){
                    console.log(err);
                } else {
                    if(saved === catalogArray.length) {
                        return callback('database populated');
                    }
                }
            });
            i++;
        }
    });
    

    更新

    如果您想为流程添加更严格的流控制,您可以使用async 模块的forEachLimit 函数将未完成的save 操作的数量限制为您指定的任何数量。例如,一次将其限制为一个未完成的save

    Catalog.remove({}, function(err){
        async.forEachLimit(catalogArray, 1, function (catalog, cb) {
            new Catalog(JSON.parse(catalog)).save(function (err, doc) {
                if (err) {
                    console.log(err);
                }
                cb(err);
            });
        }, function (err) {
            callback('database populated');
        });
    }
    

    【讨论】:

    • 感谢您的建议,但我已经尝试过了。我正在尝试在保存记录时增加 (i)。我希望让节点暂停while循环,直到记录被保存。否则,节点只会猛烈抨击猫鼬,而猫鼬就会分崩离析。我的每条数据记录都相当复杂。
    • @rob_hicks 我很惊讶这不起作用,但我添加了另一个选项,让您可以明确控制一次允许有多少 save 操作突出。
    • JohnnyHK,谢谢!那行得通。我真的很感谢你的帮助。我从未使用过异步模块。罗伯
    【解决方案2】:

    罗伯,

    简短的回答:

    您创建了一个无限循环。您正在同步思考和阻塞,Javascript 函数异步且没有阻塞。你想要做的就像试图直接把饥饿感变成三明治一样。你不能。最接近的是你用饥饿的感觉来激励你去厨房做饭。不要试图让 Javascript 阻塞。它行不通。现在,学习 async.forEachLimit。它适用于您想要在这里做的事情。

    您可能应该查看异步设计模式并更深入地理解它的含义。回调不仅仅是返回值的替代方案。它们在执行方式和执行时间上有根本的不同。这是一个很好的入门:http://cs.brown.edu/courses/csci1680/f12/handouts/async.pdf

    长答案:

    这里有一个潜在的问题,那就是您对非阻塞 IO 和异步的含义缺乏了解。我不确定您是否正在涉足节点开发,或者这只是一个一次性项目,但如果您确实打算继续使用节点(或任何异步语言),那么值得花时间了解同步和异步之间的区别设计模式,以及它们的动机。因此,这就是为什么将循环不变增量放在创建无限循环的异步回调中时出现逻辑错误的原因。

    在非计算机科学中,这意味着您对 i 的增量永远不会发生。原因是 Javascript 在调用任何异步回调之前执行单个代码块来完成。因此,在您的代码中,您的循环将一遍又一遍地运行,而我永远不会增加。而且,在后台,您一遍又一遍地将相同的文档存储在 mongo 中。循环的每次迭代都开始向 mongo 发送索引为 0 的文档,直到循环结束,回调才能触发,并且循环外的所有其他代码都运行完成。因此,回调排队。但是,您的循环再次运行,因为 i++ 从未执行(请记住,回调排队直到您的代码完成),再次插入记录 0,在您的循环完成后排队另一个回调以执行。这种情况一直持续下去,直到您的内存充满了等待通知您的无限循环文档 0 已被插入数百万次的回调。

    一般来说,如果不做一些非常糟糕的事情,就无法阻止 Javascript。例如,最重要的事情是让你的厨房着火,为我在“简短回答”中谈到的那个三明治煎一些鸡蛋。

    我的建议是利用像 async 这样的库。 https://github.com/caolan/asyncJohnnyHK 在这里提到过,他这样做是对的。

    【讨论】:

    • 安东尼,感谢您的指导。我以前看过你引用的材料。我再去读一遍。
    猜你喜欢
    • 2017-10-11
    • 2019-12-21
    • 1970-01-01
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 2015-07-13
    • 2014-04-19
    相关资源
    最近更新 更多