高速テストのためにPostgreSQLを最適化する


203

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

問題は、PGによりスペックの実行が遅くなることです。
SQLiteでは約34秒、PGでは約76秒で、2倍以上遅くなります

そこで、コードを変更せずに、SQLite同等の仕様のパフォーマンスを実現するためにいくつかの手法を適用したいと思います(理想的には接続オプションを設定するだけで、おそらく不可能です)。

私の頭の上から明らかなことは次のとおりです。

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

ご存じかもしれませんが、私は信頼性とその他については気にしていません(ここではDBは使い捨てのものです)。
私はPGを最大限に活用し、可能な限り速くする必要があります

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

更新: fsync = off + full_page_writes = off時間のみを〜65秒(〜-16秒)に短縮しました。良いスタートですが、目標の34にはほど遠いです。

アップデート2:私はRAMディスクを使用しようとしましたが、パフォーマンスゲインは誤差の範囲内でした。だから、それだけの価値はないようです。

更新3:* 最大のボトルネックを発見したので、私の仕様はSQLiteの仕様と同じくらい高速に実行されました。

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

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

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

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

SQLiteの2倍の速度向上。PGの4倍の速度増加。


2
SQLiteと同じ速さで動作するかどうかは本当に疑わしいです。シングルユーザーのSQLiteはめちゃくちゃ速いです。SQLiteの設計は非常に高速で、ユーザー数が少なく、スケーリングも不十分です。Pgの設計は適切に拡張されますが、1人のユーザーだけの単純な一括作業にはそれほど速くありません。
クレイグリンガー、

1
私はそれを理解していますが、PGを(テスト実行)用に最適化して、可能な限り高速にしたいという特別なケースがあります。そこで少し遅くなってもかまいませんが、2.2xは少し遅すぎます。どういう意味?
Dmytrii Nagirniak

+1 RAMディスクアプローチに関する更新があった場合は、その結果に非常に興味があります。
tscho

@tscho私は間違いなくここに投稿します。しかし、私は他のものに取り組んでいて、「バックグラウンド」でPGのものを「研究」しているので、しばらく時間が必要です。
Dmytrii Nagirniak 2012

される挿入データ問題や問い合わせを!あなたの質問からは明らかではありません。
a_horse_with_no_name 2012

回答:


281

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

しない

テーブルスペースをRAMdiskやその他の非永続ストレージに置かないでください

テーブルスペースを失うと、データベース全体が損傷し、大きな作業を行わないと使用が困難になる可能性があります。UNLOGGEDとにかく、テーブルを使用してキャッシュ用に大量のRAMを用意する場合と比較して、これにはほとんど利点がありません。

RAMディスクベースのシステムが本当に必要な場合initdbは、RAMディスク上のまったく新しいクラスタをinitdbな場合は、ramdisk上の新しいPostgreSQLインスタンスを実行して、ramdiskすることで、完全に使い捨てのPostgreSQLインスタンスを作成できます。

PostgreSQLサーバーの設定

テストするときは、サーバーを非耐久性でより高速な動作に設定できます。

これはfsync=off、PostgreSQL の設定で受け入れられる唯一の使用方法の1つです。この設定は、順序付けされた書き込みやその他の厄介なデータ整合性保護やクラッシュセーフ機能に煩わされないようにPostgreSQLに指示し、停電やOSのクラッシュが発生した場合にデータを完全に破棄する許可を与えます。

言うまでもなくfsync=off、他の場所から再生成できるデータの一時データベースとしてPgを使用している場合を除き、本番環境では決して有効にしないでください。fsyncをオフにしようとしている場合に限り、オフにすることもできfull_page_writesます。ことに注意してくださいfsync=offfull_page_writesで適用するクラスタレベルなので、彼らが影響を与えるすべてのあなたのPostgreSQLインスタンスにデータベースを。

本番環境で使用する場合はsynchronous_commit=off、を使用して設定することができcommit_delayます。これによりfsync=off、巨大なデータ破損のリスクがない場合と同じ利点が数多く得られます。非同期コミットを有効にすると、最近のデータがわずかに失われることがありますが、それだけです。

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

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

チューニングshared_buffersワークロードに合わせて。これはOSに依存し、マシンで他に何が起こっているかによって異なり、試行錯誤が必要です。デフォルトは非常に保守的です。shared_buffersPostgreSQL 9.2以下で増やす場合、OSの最大共有メモリ制限を増やす必要があるかもしれません。9.3以降では、共有メモリを使用してそれを回避する方法が変更されました。

多くの作業を行う接続を2、3だけ使用している場合は、値work_memを増やして、work_mem並べ替えなどに使用できるRAMを増やします。設定が高すぎると、並べ替えごとではないため、メモリ不足の問題が発生する可能性があることに注意してください。接続ごとなので、1つのクエリでネストされた並べ替えを多数持つことができます。あなただけの本当に増加する必要がありwork_memますが、中にディスクにこぼれるの種類を見ることができればEXPLAINかでログインしてlog_temp_files設定する(推奨)が、より高い値もPgは賢く計画を選んでみましょうことがあります。

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

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

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

ホストOSの調整

オペレーティングシステムレベルで実行できるチューニングもあります。あなたがしたいと思うかもしれない主なことは、ディスクへの書き込みをいつ行うかは本当に気にしないので、書き込みをディスクに積極的にフラッシュしないようにオペレーティングシステムを説得することです。

Linuxでは、これを仮想メモリサブシステムdirty_*設定で制御できますdirty_writeback_centisecs

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

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

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

クエリとワークロードのチューニング

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

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

可能な限り、一時テーブルを使用してください。WALトラフィックを生成しないため、挿入と更新の速度が大幅に向上します。大量のデータを一時テーブルに投入し、必要に応じて操作しINSERT INTO ... SELECT ...てから、ファイナルテーブルにコピーすることをお勧めします。一時テーブルはセッションごとであることに注意してください。セッションが終了するか、接続が失われると、一時テーブルは消え、他の接続はセッションの一時テーブルの内容を見ることができなくなります。

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

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

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

不要なインデックスを作成しないでください。各インデックスには保守コストがあります。インデックスの最小限のセットを使用し、ビットマップインデックススキャンでそれらを結合できるようにしてください。巨大で高価なマルチカラムインデックスを多く維持するのではなく、インデックスが必要な場合は、まずテーブルにデータを入力してから、最後にインデックスを作成してください。

ハードウェア

データベース全体を保持するのに十分なRAMがあれば、それを管理できれば大きな利益になります。

十分なRAMがない場合は、より高速なストレージでより良い結果を得ることができます。安価なSSDでも、回転する錆びとは大きく異なります。ただし、本番用の安価なSSDを信頼しないでください。クラッシュ安全ではないことが多く、データが破壊される可能性があります。

学習する

グレッグ・スミスの著書であるPostgreSQL 9.0 High Performanceは、やや古いバージョンについて言及しているにもかかわらず、依然として重要です。参考になるはずです。

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

読書:


10
@GregSmithによるPostgreSQL 9.0 High Performanceをお勧めすることもできます。この本は、ディスクレイアウトからクエリチューニングまで、パフォーマンスチューニングのあらゆる側面をカバーしており、PGの内部について非常によく理解できます。
tscho

10
PostgreSQL 9.1の本へのアップデートはリリースしませんでした。これは、9.1のパフォーマンス関連の変更がそれを保証するのに十分ではなかったためです。
グレッグ・スミス

3
素晴らしい記事。小さな更新と同様に、「shared_buffersを増やす場合は、OSの最大共有メモリ制限を増やす必要があるかもしれません」は、PostgreSQL 9.3では(ほとんどのユーザーにとって)もはや当てはまりません:postgresql.org/docs/9.3/static/release-9- 3.html#AEN114343
Gunnlaugur

1
@brauliobo私のテストでは、高いTPSで多くのtxを実行することがよくあります...同時実行が多いワークロードを含めて、本番環境をシミュレートしようとするためです。「単一接続、線形テスト」という意味であれば、私も同感です。
クレイグリンガー2015年

1
stackoverflow.com/questions/11419536/…DELETEは、行が少ないテーブルのTRUNCATEよりも高速である可能性があり、これはテストの場合に当てはまる可能性があります。
Jonathan Crosmer

9

別のディスクレイアウトを使用する:

  • $ 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およびsynchronized_commit:デフォルト値は安全ですが、データの損失を許容できる場合はオフにできます
  • random_page_cost:SSDまたは高速RAIDアレイを使用している場合、これを2.0(RAID)またはSSDの場合はさらに(1.1)に下げることができます
  • checkpoint_segments:32または64に移動して、checkpoint_completion_targetを0.9に変更できます。値を小さくすると、クラッシュ後の回復が速くなります

4
既にで実行している場合fsync=off、pg_xlogを別のディスクに配置しても、それほど改善されないことに注意してください。
intgr

SSDの値1.1は、非常に無条件のようです。これは、一部の専門家が盲目的に推奨したものであることを認めます。SSDでさえ、ランダム読み取りよりも順次読み取りの方が大幅に高速です。
Acumenus 2015

@ABBはい。ただし、OSバッファキャッシングエフェクトも機能します。とにかく、これらすべてのパラメーターは少し手波です...
Craig Ringer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.