Postgres 9.2でwork_memおよびshared_buffersを増やすと、クエリが大幅に遅くなります


39

16 GBのRAMを搭載した8コアのRHEL 6.3で実行されているPostgreSQL 9.2インスタンスがあります。サーバーはこのデータベース専用です。デフォルトのpostgresql.confはメモリ設定に関してかなり保守的であるため、Postgresがより多くのメモリを使用できるようにすることは良い考えだと思いました。驚いたことに、wiki.postgresql.org / wiki / Tuning_Your_PostgreSQL_Serverのアドバイスに従うと、実際に実行するすべてのクエリが大幅に遅くなりましたが、より複雑なクエリでは明らかに目立ちます。

pgtuneの実行も試してみました。これは、調整されたパラメーターを増やして次の推奨事項を提示しましたが、何も変わりませんでした。RAMサイズの1/4のshared_buffersが推奨されますが、これは他のアドバイス(特にPG wiki)に沿っているようです。

default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80

設定を変更した後(を使用してreindex database)データベース全体のインデックスを再作成しようとしましたが、それも役に立ちませんでした。shared_buffersとwork_memをいじりました。非常に控えめなデフォルト値(128k / 1MB)から徐々に変更すると、パフォーマンスが徐々に低下します。

私は走ったEXPLAIN (ANALYZE,BUFFERS)いくつかのクエリにし、犯人はハッシュが大幅に遅くなりましょうということのようです。理由は明らかではありません。

特定の例を挙げると、次のクエリがあります。デフォルト構成で約2100ミリ秒、バッファーサイズを増やした構成で約3300ミリ秒で実行されます。

select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';

EXPLAIN (ANALYZE,BUFFERS) 上記のクエリの場合:

問題は、バッファサイズを増やすとパフォーマンスが低下するのなぜですか?マシンのメモリが不足していません。OSの共有メモリが(shmmaxおよびshmall)の値が非常に大きな値に設定されている場合の割り当ては、問題になりません。Postgresログにもエラーが記録されていません。私はデフォルト設定でautovacuumを実行していますが、それとは何の関係もないと思います。すべてのクエリは、構成が変更された(PGが再起動された)だけで、同じマシンで数秒間隔で実行されました。

編集:特に興味深い事実を見つけました。2010年半ばのiMac(OSX 10.7.5)でPostgres 9.2.1と16GB RAMを使用して同じテストを実行しても、スローダウンは発生しません。具体的には:

set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms

サーバー上でまったく同じデータを使用してまったく同じクエリ(上記のクエリ)を実行すると、work_mem = 1MBで2100ミリ秒、96 MBで3200ミリ秒になります。

MacにはSSDが搭載されているため、当然ながら高速ですが、期待どおりの動作を示します。

pgsql-performanceに関するフォローアップの議論も参照してください。


1
遅いケースでは、すべてのステップが一貫して遅いようです。他の設定は同じままですか?
-dezso

1
これは一般的なフォーラムではなく、より専門的なフォーラムでこれを尋ねる価値があるでしょう。この場合、pgsql-generalメーリングリストarchives.postgresql.org/pgsql-general
Colin 't Hart

1
ああ、返事をして、答えを見つけたら自分の質問に答えてください!(これは許可されており、奨励されています)。
コリン 'tハート

1
この点で、PostgresはOracleにどのように似ているのだろうかと思います。ジョナサンルイス(Oracleの第一人者)によるコースを覚えています。詳細を忘れてしまいましたが、Oracleが部分的な並べ替えを行い、それらを一時的なストレージに書き出し、後でそれらを結合することに関係がありました。どういうわけか、より多くのメモリがこのプロセスを遅くしました。
コリン

2
質問は現在pgsql-performanceに投稿されています:archives.postgresql.org/pgsql-performance/2012-11/msg00004.php
Petr Praus

回答:


28

まず、work_memは操作ごとであるため、すぐに過剰になる可能性があることに注意してください。一般に、ソートの速度が遅くなる問題がない場合は、必要になるまでwork_memをそのままにしておきます。

クエリプランを見ると、印象的なことの1つは、2つのプランを見るとバッファヒットが大きく異なり、シーケンシャルスキャンでさえ遅いことです。この問題は先読みキャッシングに関係しており、そのためのスペースが少ないと思われます。これは、インデックスの再利用とディスク上のテーブルの読み取りに対してメモリをバイアスしていることを意味します。


私の理解では、OSキャッシュにそのページが含まれるかどうかは実際にはわからないので、PostgreSQLはディスクからページを読み取る前にページのキャッシュを検索します。ページはキャッシュ内にとどまり、そのキャッシュはOSキャッシュより遅いため、これにより、高速のクエリと低速のクエリの種類が変わります。実際、work_memの問題は別として、プランを読むと、すべてのクエリ情報がキャッシュから取得されているように見えますが、どのキャッシュの問題なのでしょうか。

work_mem:ソートまたは関連する結合操作に割り当てることができるメモリの量。これは、ステートメントごとまたはバックエンドごとではなく、操作ごとです。そのため、単一の複雑なクエリでは、この量のメモリを何度も使用できます。この制限に達しているかどうかは明らかではありませんが、注意して認識しておく価値はあります。これを大きくしすぎると、読み取りキャッシュと共有バッファーに使用できるメモリが失われます。

shared_buffers:実際のPostgreSQLページキューに割り当てるメモリ量。理想的には、データベースの興味深いセットは、ここにキャッシュされたメモリと読み取りバッファに残っています。ただし、これにより、すべてのバックエンドで最も頻繁に使用される情報がキャッシュされ、ディスクにフラッシュされなくなります。Linuxでは、このキャッシュはOSディスクキャッシュよりも大幅に遅くなりますが、OSディスクキャッシュがPostgreSQLに対して透過的でないことを保証します。これはあなたの問題がどこにあるかかなりはっきりしています。

PostgreSQLにはこのキャッシュに関する深い知識があるため、リクエストがあると、まず共有バッファがチェックされ、ページが検索されます。それらが存在しない場合、OSにファイルからそれらを開くように依頼し、OSが結果をキャッシュした場合、キャッシュされたコピーを返します(これは共有バッファーよりも高速ですが、Pgはそれがキャッシュされているかどうかを判断できませんディスク、およびディスクははるかに遅いため、PostgreSQLは通常その機会を取りません。これは、ランダムページアクセスとシーケンシャルページアクセスにも影響することに注意してください。したがって、shared_buffersの設定を低くすると、パフォーマンスが向上する場合があります。

私の直感では、shared_buffer設定を大きくすると、同時実行性の高い環境でパフォーマンスが向上するか、少なくとも一貫性が向上するでしょう。また、PostgreSQLはこのメモリを取得して保持するため、システム上で他の処理を実行している場合、読み取りバッファは他のプロセスによって読み取られたファイルを保持することに注意してください。非常に大規模で複雑なトピックです。共有バッファ設定を大きくすると、パフォーマンスの保証が向上しますが、場合によってはパフォーマンスが低下する可能性があります。


10

増加work_memするとパフォーマンスが低下するという逆説的な効果(@Chrisに説明があるかもしれません)とは別に、少なくとも2つの方法で機能を改善できます。

  • 2つの偽物LEFT JOINJOIN。これは、クエリプランナーを混乱させ、計画の質を低下させる可能性があります。

SELECT count(*) AS ct
FROM   contest            c
JOIN   contestparticipant cp ON cp.contestId = c.id
JOIN   personinfo         pi ON pi.id = cp.personinfoid
LEFT   JOIN teammember    tm ON tm.contestparticipantid = cp.id
LEFT   JOIN staffmember   sm ON sm.contestparticipantid = cp.id
LEFT   JOIN person        p  ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR     pi.lastname  LIKE '%b%')
  • 実際の検索パターンがより選択的であると仮定して、トライグラムインデックスを使用してpi.firstnamepi.lastname非アンカーLIKE検索をサポートします。(次のような短いパターン'%a%'もサポートされていますが、インデックスは非選択的な述語には役立ちそうにありません。):

CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx  ON personinfo USING gin (lastname gin_trgm_ops);

または、1つのマルチカラムインデックス:

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

クエリをかなり高速にする必要があります。これには追加のモジュールpg_trgmをインストールする必要があります。これらの関連質問の下の詳細:


また、work_mem ローカルに設定しようとしました-現在のトランザクションに対してのみですか?

SET LOCAL work_mem = '96MB';

これにより、同時トランザクションがより多くのRAMを消費し、場合によっては互いに飢えさせることを防ぎます。


3
Erwinのローカルwork_mem提案を2回目にしたいと思います。work_memはより高速なクエリの種類を変更するため、一部のクエリでは変更する必要がある場合があります。つまり、work_memレベルが低いと、少数のレコードを複雑な方法でソート/結合するクエリ(つまり、多数の結合)に最適です。一方、work_memレベルが高いと、一度に多数の行をソートまたは結合するクエリに最適です。
クリストラバーズ

その間、クエリを改善しました(質問は昨年10月のものです)。ありがとうございます。クエリは、主に効果を示すのに役立ちます。インデックスのヒントをありがとう、それを試してみます!
ペトルプラウス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.