web-dev-qa-db-ja.com

高速テスト用にPostgreSQLを最適化する

典型的なRailsアプリケーションのために、SQLiteからPostgreSQLに切り替えています。

問題は、PGで仕様の実行が遅くなったことです。
SQLiteでは〜34秒かかりましたが、PGでは〜76秒で、2倍以上遅いです。

そこで、いくつかのテクニックをに適用し、コードを変更せずに仕様のパフォーマンスをSQLiteと同等にします(接続オプションを設定するだけで、おそらく不可能です)。

私の頭の上からの明白な事柄のカップルは次のとおりです:

  • RAMディスク(OSXでのRSpecを使用した適切なセットアップが表示されるとよいでしょう)
  • ログに記録されていないテーブル(データベース全体に適用できるので、すべてのスクリプトを変更する必要はありませんか?)

ご存知かもしれませんが、私は信頼性とそれ以外のことは気にしません(DBはここではただの捨てものです)。
PGを最大限に活用し、できる限り速くにする必要があります。

ベストアンサーは、理想的には、それを行うためのtricksを説明し、それらのトリックのセットアップと欠点を説明します。

PDATE:fsync = off + full_page_writes = offは、時間を〜65秒(〜-16秒)に減らしただけです。良いスタートですが、34の目標からはほど遠いです。

PDATE 2:I RAM diskを使用しようとしました しかし、パフォーマンスの向上はエラーマージン内でした。そのため、価値があるとは思えません。

PDATE 3:*最大のボトルネックが見つかりましたが、現在の仕様はSQLiteの仕様と同じくらい高速に実行されます。

問題は切り捨てを行ったデータベースのクリーンアップでした。どうやらSQLiteの速度が速すぎるようです。

それを「修正」するには、各テストの前にトランザクションを開き、最後にロールバックします。

〜700テストのいくつかの数値。

  • 切り捨て:SQLite-34秒、PG-76秒。
  • トランザクション:SQLite-17秒、PG-18秒。

SQLiteの2倍の速度向上。 PGの速度が4倍になりました。

194

まず、常に最新バージョンのPostgreSQLを使用します。パフォーマンスの改善は常に行われているため、古いバージョンをチューニングしている場合はおそらく時間を無駄にしているでしょう。たとえば、 PostgreSQL 9.2はTRUNCATE の速度を大幅に改善し、もちろんインデックスのみのスキャンを追加します。マイナーリリースでも常に従う必要があります。 バージョンポリシー を参照してください。

いけない

Doではなく、RAMdiskまたはその他の非永続ストレージにテーブルスペースを置きます

表領域が失われると、データベース全体が破損し、大きな作業なしで使用するのが困難になる場合があります。 UNLOGGEDテーブルを使用し、とにかくキャッシュ用にRAMをたくさん持つことに比べると、これにはほとんど利点がありません。

本当にramdiskベースのシステムが必要な場合は、initdb ramdisk上の新しいPostgreSQLインスタンスをinitdbingすることにより、ramdisk上のまったく新しいクラスターになります。したがって、完全に破棄可能なPostgreSQLインスタンスができます。

PostgreSQLサーバーの構成

テスト時には、 非耐久性で高速な動作 用にサーバーを構成できます。

これは、PostgreSQLの fsync=off 設定の唯一の許容可能な使用法の1つです。この設定は、PostgreSQLに、順序付けられた書き込みや、その他の厄介なデータ整合性保護やクラッシュ安全性のものを気にせず、電源が切れたりOSがクラッシュした場合にデータを完全に破棄する許可を与えます。

言うまでもなく、Pgを他の場所から再生成できるデータの一時データベースとして使用していない限り、本番環境でfsync=offを有効にしないでください。 fsyncをオフにしようとしている場合にのみ、 full_page_writes をオフにすることもできます。 fsync=offfull_page_writesclusterレベルで適用されるため、PostgreSQLインスタンスのallデータベースに影響することに注意してください。

実稼働で使用する場合は、synchronous_commit=offを使用してcommit_delayを設定できます。これは、fsync=offと同じ利点の多くを、巨大なデータ破損のリスクなしに得ることができるためです。非同期コミットを有効にすると、最近のデータが少し失われますが、それだけです。

DDLをわずかに変更するオプションがある場合、Pg 9.1+のUNLOGGEDテーブルを使用して、WALロギングを完全に回避し、サーバーがクラッシュした場合にテーブルが消去されるという犠牲を払って実際の速度を上げることもできます。すべてのテーブルのログを記録しないようにする構成オプションはありません。CREATE TABLE中に設定する必要があります。テストに適していることに加えて、安全である必要があるものが含まれているデータベースに生成されたデータまたは重要でないデータでいっぱいのテーブルがある場合に便利です。

ログを確認し、チェックポイントが多すぎるという警告が表示されているかどうかを確認します。もしそうなら、 checkpoint_segments を増やす必要があります。また、checkpoint_completion_targetを調整して書き込みをスムーズにすることもできます。

ワークロードに合わせてshared_buffersを調整します。これはOSに依存し、マシンで他に何が起こっているかに依存し、試行錯誤が必要です。デフォルトは非常に控えめです。 PostgreSQL 9.2以前でshared_buffersを増やす場合、OSの最大共有メモリ制限を増やす必要がある場合があります。 9.3以降では、共有メモリの使用方法を変更して、それを回避しました。

多くの作業を行う接続を2、3個だけ使用している場合は、work_memを増やして、並べ替えなどに使用するRAMを増やします。work_mem設定が高すぎると、メモリ不足の問題が発生する可能性がありますこれは、接続ごとではなくソートごとであるため、1つのクエリがネストされたソートを多数持つことができるためです。reallyは、EXPLAINでディスクに流出するソートを確認できる場合、または work_mem設定 で記録される場合(推奨)、log_temp_filesを増やす必要があります、しかし、より高い値は、Pgがよりスマートな計画を選択できるようにする場合もあります。

ここで別のポスターで述べたように、可能であればxlogとメインテーブル/インデックスを別々のHDDに置くのが賢明です。個別のパーティションはかなり無意味です。本当に個別のドライブが必要です。 fsync=offを使用して実行している場合、この分離によるメリットははるかに少なく、UNLOGGEDテーブルを使用している場合はほとんどありません。

最後に、クエリを調整します。 random_page_costおよびseq_page_costがシステムのパフォーマンスを反映していること、effective_cache_sizeが正しいことなどを確認してください。EXPLAIN (BUFFERS, ANALYZE)を使用して個々のクエリプランを調べ、auto_explainモジュールをオンにしてすべての低速クエリを報告します。適切なインデックスを作成するか、コストパラメータを調整するだけで、クエリのパフォーマンスを劇的に向上させることができます。

私の知る限り、データベースまたはクラスタ全体をUNLOGGEDとして設定する方法はありません。そうすることができるのは面白いでしょう。 PostgreSQLメーリングリストで質問することを検討してください。

ホストOSのチューニング

オペレーティングシステムレベルで実行できるチューニングもいくつかあります。あなたがしたい主なことは、オペレーティングシステムがディスクへの書き込みを積極的にフラッシュしないことを確信させることです。

Linuxでは、 virtual memory subsystemdirty_*のようなdirty_writeback_centisecs設定でこれを制御できます。

ライトバック設定の調整が緩すぎる唯一の問題は、他のプログラムによるフラッシュにより、PostgreSQLのすべての蓄積バッファもフラッシュされ、すべてが書き込みでブロックされている間に大きなストールを引き起こす可能性があることです。別のファイルシステムでPostgreSQLを実行することでこれを軽減できる場合がありますが、一部のフラッシュはファイルシステムレベルではなくデバイスレベルまたはホスト全体レベルであるため、それに頼ることはできません。

このチューニングでは、実際にワークロードに最適なものを確認するために設定をいじる必要があります。

新しいカーネルでは、vm.zone_reclaim_modeをゼロに設定することをお勧めします。これは、PostgreSQLがshared_buffersを管理する方法との相互作用により、NUMAシステム(最近のほとんどのシステム)で深刻なパフォーマンス問題を引き起こす可能性があるためです。

クエリとワークロードの調整

これらはコードの変更が必要なものです。彼らはあなたに合わないかもしれません。いくつかはあなたが適用できるかもしれないものです。

作業をより大きなトランザクションにバッチ処理しない場合は、開始します。小さなトランザクションの多くは高価であるため、可能な限り実用的であれば、バッチ処理を行う必要があります。非同期コミットを使用している場合、これはそれほど重要ではありませんが、それでも強くお勧めします。

可能な限り一時テーブルを使用します。 WALトラフィックを生成しないため、挿入および更新の速度が大幅に向上します。大量のデータを一時テーブルに丸lurみし、必要に応じて操作してから、INSERT INTO ... SELECT ...を実行して最終テーブルにコピーする価値がある場合があります。一時テーブルはセッションごとです。セッションが終了するか接続が失われると、一時テーブルはなくなり、他の接続はセッションの一時テーブルの内容を見ることができなくなります。

PostgreSQL 9.1以降を使用している場合は、セッション状態など、失うことのできるデータに UNLOGGED テーブルを使用できます。これらは異なるセッションで表示され、接続間で保持されます。サーバーが不意にシャットダウンして再作成できないものには使用できない場合は切り捨てられますが、キャッシュ、マテリアライズドビュー、状態テーブルなどには最適です。

一般に、DELETE FROM blah;は使用しないでください。代わりにTRUNCATE TABLE blah;を使用してください。テーブル内のすべての行をダンプする方がはるかに高速です。可能であれば、1回のTRUNCATE呼び出しで多くのテーブルを切り捨てます。ただし、TRUNCATESの小さなテーブルを何度も繰り返し使用する場合は注意が必要です。参照: Postgresql切り捨て速度

外部キーにインデックスがない場合、それらの外部キーによって参照される主キーを含むDELETEsは恐ろしく遅くなります。参照されるテーブルからDELETEを期待する場合は、そのようなインデックスを作成してください。 TRUNCATEにはインデックスは必要ありません。

不要なインデックスを作成しないでください。各インデックスにはメンテナンスコストがあります。インデックスの最小限のセットを使用し、ビットマップインデックススキャンでそれらを組み合わせて、巨大で高価なマルチカラムインデックスを多数維持するのではなく、それらを組み合わせてみてください。インデックスが必要な場合は、最初にテーブルを作成してから、最後にインデックスを作成してください。

ハードウェア

データベース全体を保持するのに十分なRAMがあると、データベースを管理できれば大きなメリットになります。

十分なRAMがない場合は、ストレージが高速になるほど良くなります。安価なSSDでさえ、Rustの回転に大きな違いをもたらします。しかし、安価なSSDを本番用に信頼しないでください。それらは多くの場合、クラッシュセーフではなく、データを破壊する可能性があります。

学習

グレッグ・スミスの本 PostgreSQL 9.0 High Performance は、やや古いバージョンに言及しているにも関わらず、関連性があります。役立つリファレンスになるはずです。

PostgreSQLの一般的なメーリングリストに参加してフォローしてください。

読書:

268
Craig Ringer

別のディスクレイアウトを使用します。

  • $ PGDATA用の異なるディスク
  • $ PGDATA/pg_xlog用の異なるディスク
  • temファイル用の異なるディスク(データベース$ PGDATA/base // pgsql_tmpごと)(work_memに関する注意を参照)

postgresql.confの調整:

  • shared_memory:使用可能なRAMの30%、ただし6〜8GB以下。書き込み集中型のワークロードでは、共有メモリ(2GB-4GB)が少ない方が良いようです
  • work_mem:主にソート/集計を使用した選択クエリ用。これは接続ごとの設定であり、クエリはその値を複数回割り当てることができます。データが収まらない場合は、ディスクが使用されます(pgsql_tmp)。 「explain analyze」をチェックして、必要なメモリ量を確認します
  • fsyncおよびsynchronous_commit:デフォルト値は安全ですが、データの損失を許容できる場合は、オフにすることができます
  • random_page_cost:SSDまたは高速RAIDアレイがある場合、これを2.0(RAID)またはSSDの場合はさらに低く(1.1)に下げることができます
  • checkpoint_segments:32または64を大きくして、checkpoint_completion_targetを0.9に変更できます。値を小さくすると、クラッシュ後の回復が速くなります
9
mys