2

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 にロードする最良の方法は何ですか?

ロブ

4

2 に答える 2

4

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処理の操作の数を指定した数に制限できます。たとえば、一度に 1 つの未処理に制限するには、次のようにします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');
    });
}
于 2013-01-19T19:23:59.483 に答える
2

ロブ、

簡単な答え:

無限ループを作成しました。あなたは同期的でブロッキングを考えていますが、Javascript は非同期的でブロッキングなしで機能します。あなたがやろうとしていることは、空腹感を直接サンドイッチに変えようとしているようなものです. できません。最も近いのは、空腹感を利用して、キッチンに行って作るように動機付けることです。Javascript をブロックしようとしないでください。うまくいきません。次に、async.forEachLimit について学習します。ここでやりたいことがうまくいきます。

おそらく、非同期設計パターンを確認し、より深いレベルでそれが何を意味するかを理解する必要があります。コールバックは、戻り値の単なる代替手段ではありません。それらは、実行される方法とタイミングが根本的に異なります。ここに良い入門書があります: http://cs.brown.edu/courses/csci1680/f12/handouts/async.pdf

長い答え:

ここには根本的な問題があります。それは、ノンブロッキング IO と非同期が何を意味するのかを理解していないことです。ノード開発に取り掛かるのか、それともこれが 1 回限りのプロジェクトなのかはわかりませんが、ノード (または任意の非同期言語) を引き続き使用する予定がある場合は、同期と非同期の違いを理解する価値があります。設計パターン、およびそれらにどのような動機があるか。そのため、無限ループを作成している非同期コールバック内にループ不変インクリメントを配置すると、論理エラーが発生します。

非コンピュータ サイエンスでは、これは i へのインクリメントが発生しないことを意味します。その理由は、非同期コールバックが呼び出される前に、JavaScript が単一のコード ブロックを最後まで実行するためです。したがって、コードでは、インクリメントすることなく、ループが何度も実行されます。そして、バックグラウンドで、同じドキュメントをmongoに何度も保存しています。ループの反復ごとにインデックス 0 のドキュメントの mongo への送信が開始され、コールバックはループが終了するまで起動できず、ループ外の他のすべてのコードは完了するまで実行されます。したがって、コールバックはキューに入れられます。ただし、i++ が実行されないため (コードが終了するまでコールバックがキューに入れられることを思い出してください)、ループが再び実行され、レコード 0 が再び挿入され、ループが完了した後に実行する別のコールバックがキューに入れられます。

一般に、本当に悪いことをしない限り Javascript をブロックする方法はありません。たとえば、「簡単な答え」で説明したサンドイッチ用の卵を揚げるために、キッチンに火を点けることが最も重要です。

私のアドバイスは、非同期のようなライブラリを利用することです。https://github.com/caolan/async JohnnyHK がここで言及しましたが、彼はそうするのが正しかったです。

于 2013-01-20T19:06:52.610 に答える