261

Postgres の挿入パフォーマンスをテストしています。データ型として数値を持つ 1 つの列を持つテーブルがあります。その上に索引もあります。次のクエリを使用してデータベースを埋めました。

insert into aNumber (id) values (564),(43536),(34560) ...

上記のクエリを使用して、400 万行を一度に 10,000 行挿入しました。データベースが 600 万行に達した後、パフォーマンスは 15 分ごとに 100 万行に大幅に低下しました。挿入性能を上げるコツはありますか?このプロジェクトで最適な挿入パフォーマンスが必要です。

5 GB RAM のマシンで Windows 7 Pro を使用。

4

7 に答える 7

546

PostgreSQL マニュアルのpopulate a database 、このトピックに関する depesz の優れた記事、およびこの SO questionを参照してください。

(この回答は、既存の DB へのデータの一括読み込み、または新しい DB の作成に関するものであることに注意してください。DB の復元パフォーマンスpg_restoreまたは出力psqlの実行に関心がある場合は、作成などのことを既に行っているpg_dumpため、この多くは当てはまりません。スキーマとデータの復元が完了した後のトリガーとインデックス)pg_dumppg_restore

やるべきことはたくさんあります。理想的な解決策は、インデックスのないテーブルにインポートしUNLOGGEDてから、ログに記録するように変更してインデックスを追加することです。残念ながら、PostgreSQL 9.4 では、テーブルUNLOGGEDをログに記録するように変更することはサポートされていません。9.5 ではALTER TABLE ... SET LOGGED、これを実行できるように追加されています。

一括インポートのためにデータベースをオフラインにできる場合は、pg_bulkload.

さもないと:

  • テーブルのトリガーを無効にする

  • インポートを開始する前にインデックスを削除し、後で再作成します。(同じデータを段階的に追加するよりも、1 回のパスでインデックスを構築する方がはるかに時間がかからず、結果のインデックスははるかにコンパクトになります)。

  • 単一のトランザクション内でインポートを実行する場合は、外部キー制約を削除し、インポートを実行して、コミットする前に制約を再作成しても安全です。無効なデータを導入する可能性があるため、インポートが複数のトランザクションに分割されている場合は、これを行わないでください。

  • 可能であれば、s のCOPY代わりに使用しますINSERT

  • 使用できない場合は、実用的であればCOPY多値の s の使用を検討してください。INSERTあなたはすでにこれをやっているようです。ただし、1 つの値に多くの値をリストしよとしないでください。VALUESこれらの値は数回にわたってメモリに収まる必要があるため、ステートメントごとに数百に抑えてください。

  • 挿入を明示的なトランザクションにバッチ処理し、トランザクションごとに数十万または数百万の挿入を行います。実用的な制限はありませんが、バッチ処理を使用すると、入力データの各バッチの開始をマークすることでエラーから回復できます。繰り返しますが、あなたはすでにこれを行っているようです。

  • synchronous_commit=offと hugeを使用して、 commit_delayfsync() のコストを削減します。ただし、作業を大きなトランザクションにバッチ処理した場合、これはあまり役に立ちません。

  • INSERTまたはCOPY複数の接続から並行して。ハードウェアのディスク サブシステムに依存する数。経験則として、直接接続ストレージを使用する場合は、物理ハード ドライブごとに 1 つの接続が必要です。

  • 高いmax_wal_size値を設定して (checkpoint_segments古いバージョンの場合)、有効にしlog_checkpointsます。PostgreSQL のログを調べて、チェックポイントが頻繁に発生しているというメッセージが表示されていないことを確認してください。

  • インポート中にシステムがクラッシュした場合に、PostgreSQL クラスター全体 (データベースと同じクラスター上の他のすべて) が壊滅的な破損によって失われても構わない場合に限り、Pg を停止し、 を設定fsync=offし、Pg を開始して、インポートを実行します。次に、(非常に) Pg を停止し、fsync=on再度設定します。WAL 設定を参照してください。PostgreSQL インストールのデータベースに必要なデータが既にある場合は、これを行わないでください。設定すると、設定fsync=offすることもできますfull_page_writes=off。繰り返しますが、データベースの破損やデータの損失を防ぐために、インポート後に必ずオンに戻してください。Pg マニュアルの非永続設定を参照してください。

システムのチューニングも検討する必要があります。

  • ストレージには、できるだけ高品質のSSD を使用してください。信頼性の高い、電力保護されたライトバック キャッシュを備えた優れた SSD は、コミット レートを信じられないほど高速にします。上記のアドバイスに従った場合、それらはあまり有益ではありません-ディスクフラッシュ/fsync()の数を減らします-それでも大きな助けになる可能性があります. データの保持に関心がない場合を除き、適切な停電保護なしで安価な SSD を使用しないでください。

  • 直接接続ストレージに RAID 5 または RAID 6 を使用している場合は、今すぐ停止してください。データをバックアップし、RAID アレイを RAID 10 に再構築して、再試行してください。RAID 5/6 は、一括書き込みのパフォーマンスには望みがありませんが、大きなキャッシュを備えた優れた RAID コントローラーが役立ちます。

  • バッテリでバックアップされた大きなライトバック キャッシュを備えたハードウェア RAID コントローラを使用するオプションがある場合、これにより、多くのコミットを伴うワークロードの書き込みパフォーマンスが大幅に向上します。commit_delay で非同期コミットを使用している場合、または一括読み込み中に大きなトランザクションをあまり実行していない場合は、それほど役に立ちません。

  • 可能であれば、別のディスク/ディスク アレイにWAL ( pg_wal、または古いバージョン) を格納します。pg_xlog同じディスクで別のファイルシステムを使用してもほとんど意味がありません。多くの場合、WAL に RAID1 ペアを使用することを選択します。繰り返しますが、これはコミット率の高いシステムでより大きな効果があり、ログに記録されていないテーブルをデータ ロード ターゲットとして使用している場合はほとんど効果がありません。

また、テストを高速化するために PostgreSQL を最適化する にも興味があるかもしれません。

于 2012-08-30T23:58:56.250 に答える
16

COPY table TO ... WITH BINARYドキュメントによると、「テキストや CSV 形式よりも多少高速です」を使用してください。これは、挿入する行が数百万あり、バイナリ データに慣れている場合にのみ行ってください。

バイナリ入力で psycopg2 を使用した Python のレシピの例を次に示します。

于 2014-06-13T07:05:09.657 に答える
2

最適な挿入パフォーマンスを得るには、オプションである場合はインデックスを無効にします。それ以外に、より良いハードウェア (ディスク、メモリ) も役立ちます

于 2012-08-30T22:43:06.330 に答える