更新
その後、この回答は次の記事に置き換えられました:データのインポート> 、これは最新のアプローチを表しています。
シナリオを再現するために、pg-promise を使用しました ライブラリ、そしてそれを正面から試すことは決して機能しないことを確認できます。どのライブラリを使用しても、それが重要なアプローチです。
以下は、挿入をチャンクに分割してから、トランザクション内の各チャンクを実行する変更されたアプローチです。これは、負荷分散(別名スロットル)です。
function insertRecords(N) {
return db.tx(function (ctx) {
var queries = [];
for (var i = 1; i <= N; i++) {
queries.push(ctx.none('insert into test(name) values($1)', 'name-' + i));
}
return promise.all(queries);
});
}
function insertAll(idx) {
if (!idx) {
idx = 0;
}
return insertRecords(100000)
.then(function () {
if (idx >= 9) {
return promise.resolve('SUCCESS');
} else {
return insertAll(++idx);
}
}, function (reason) {
return promise.reject(reason);
});
}
insertAll()
.then(function (data) {
console.log(data);
}, function (reason) {
console.log(reason);
})
.done(function () {
pgp.end();
});
これにより、約4分で1000,000レコードが生成され、最初の3つのトランザクションの後で劇的に遅くなりました。約340MBのメモリを消費するNodeJS0.10.38(64ビット)を使用していました。このようにして、100,000レコードを10回続けて挿入しました。
同じことを行うと、今回だけ100トランザクション内に10,000レコードが挿入され、同じ1,000,000レコードがわずか1分25秒で追加され、速度が低下することはありません。ノードJSは約100 MBのメモリを消費します。これは、このようなデータのパーティショニングが非常に重要であることを示しています。良い考えです。
どのライブラリを使用するかは関係ありません。アプローチは同じである必要があります:
- 挿入物を複数のトランザクションに分割/スロットルします。
- 1回のトランザクションで挿入のリストを約10,000レコードに保持します。
- すべてのトランザクションを同期チェーンで実行します。
- 各トランザクションのCOMMIT後に、接続をプールに解放します。
これらのルールのいずれかに違反した場合、トラブルが保証されます。たとえば、ルール3に違反すると、Node JSプロセスのメモリがすぐに不足し、エラーがスローされる可能性があります。私の例のルール4は、ライブラリによって提供されました。
また、このパターンに従うと、接続プールの設定に悩む必要はありません。
更新1
pg-promise の新しいバージョン 以下に示すように、このようなシナリオを完全にサポートします。
function factory(index) {
if (index < 1000000) {
return this.query('insert into test(name) values($1)', 'name-' + index);
}
}
db.tx(function () {
return this.batch([
this.none('drop table if exists test'),
this.none('create table test(id serial, name text)'),
this.sequence(factory), // key method
this.one('select count(*) from test')
]);
})
.then(function (data) {
console.log("COUNT:", data[3].count);
})
.catch(function (error) {
console.log("ERROR:", error);
});
また、テーブルの作成など、余分なものを含めたくない場合は、さらに簡単に見えます。
function factory(index) {
if (index < 1000000) {
return this.query('insert into test(name) values($1)', 'name-' + index);
}
}
db.tx(function () {
return this.sequence(factory);
})
.then(function (data) {
// success;
})
.catch(function (error) {
// error;
});
同期トランザクション を参照してください 詳細については。
Bluebird
の使用 たとえば、promiseライブラリとして、本番マシンで1,000,000レコードを挿入するのに1分43秒かかります(長いスタックトレースを有効にしない場合)。
あなたはただあなたのfactory
を持っているでしょう メソッドは、index
に従ってリクエストを返します 、残りがなくなるまで、そのように簡単です。
そして最良の部分は、これは高速であるだけでなく、NodeJSプロセスにほとんど負荷をかけないことです。メモリテストプロセスは、テスト全体を通じて60MB未満にとどまり、CPU時間の7〜8%しか消費しません。
アップデート2
バージョン1.7.2以降、 pg-promise 超大規模なトランザクションを簡単にサポートします。 同期トランザクション の章を参照してください。 。
たとえば、Windows 8.1 64ビットを使用している自宅のPCでは、1回のトランザクションでわずか15分で10,000,000レコードを挿入できます。
テストでは、PCを本番モードに設定し、Bluebird を使用しました。 約束のライブラリとして。テスト中、NodeJS 0.12.5プロセス全体(64ビット)でメモリ消費量が75MBを超えることはありませんでしたが、i7-4770 CPUは一貫して15%の負荷を示しました。
同じ方法で100mのレコードを挿入するには、忍耐力が必要ですが、コンピュータリソースは必要ありません。
その間に、1mインサートの以前のテストは1m43sから1m31sに低下しました。
アップデート3
次の考慮事項が大きな違いを生む可能性があります:パフォーマンスブースト 。
アップデート4
関連する質問、より良い実装例:pg-promiseを使用した大規模な挿入 。
アップデート5
より良い新しい例はここにあります:nodeJS挿入データPostgreSQLエラーに