Postgresがインデックスを使用するように強制する方法
多くのデータベースで見られる一般的な「インデックスヒント」機能について尋ねていると仮定すると、PostgreSQLはそのような機能を提供しません。これは、PostgreSQLチームが行った意識的な決定でした。その理由とその代わりにできることの概要は here にあります。その理由は、基本的には、データの変更に伴って後の段階でより多くの問題を引き起こす傾向があるパフォーマンスハックであり、PostgreSQLのオプティマイザーは統計に基づいて計画を再評価できるためです。言い換えると、今日の良いクエリプランになる可能性があるものは、おそらく常に良いクエリプランではなく、インデックスヒントは常に特定のクエリプランを強制します。
テストに役立つ非常に鈍いハンマーとして、enable_seqscan
およびenable_indexscan
パラメーターを使用できます。見る:
これらは、進行中の本番使用には適していません。クエリプランの選択に問題がある場合は、 クエリパフォーマンスの問題を追跡するためのドキュメント が表示されます。 enable_
paramsを設定してすぐに立ち去らないでください。
インデックスを使用する理由が特にない限り、Postgresが正しい選択をしている可能性があります。どうして?
この古いニュースグループの投稿 も参照してください。
おそらく使用する唯一の正当な理由
set enable_seqscan=false
クエリを書いているときに、テーブルに大量のデータがあった場合にクエリプランが実際にどうなるかをすばやく確認したい場合です。もちろん、データセットが小さすぎるという理由だけで、クエリがインデックスを使用していないことをすばやく確認する必要がある場合。
PostgreSQLは、特定の条件に対して最適なインデックスの選択に失敗することがあります。例として、数百万行のトランザクションテーブルがあり、その中には特定の日に数百行があり、テーブルには、transaction_id、client_id、date、descriptionの4つのインデックスがあるとします。次のクエリを実行します。
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQLは、transactions_date_idxの代わりにインデックスtransaction_description_idxを使用することを選択する場合があります。これにより、クエリが1秒未満ではなく数分かかる場合があります。この場合、次のように条件を変更することにより、日付のインデックスを強制的に使用できます。
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
質問自体は非常に無効です。強制(たとえば、enable_seqscan = offを実行する)は非常に悪い考えです。速くなるかどうかを確認するのは便利かもしれませんが、実稼働コードではこのようなトリックを使用しないでください。
代わりに-クエリの分析を説明し、それを読んで、PostgreSQLが(あなたの意見では)悪い計画を選択する理由を見つけてください。
Webには、説明分析出力の読み取りに役立つツールがあります。そのうちの1つは explain.depesz.com -私が作成しました。
別のオプションは、 freenode ircネットワークで#postgresqlチャネルに参加し、そこにいる人たちと話をして手助けすることです。それは、会話のようなものであり、確認すべき多くのこと、学ぶべき多くのことを備えています。
この問題は通常、インデックススキャンの推定コストが高すぎて現実を正しく反映していない場合に発生します。これを修正するには、random_page_cost
構成パラメーターを下げる必要がある場合があります。 Postgresドキュメント から:
この値を減らすと[...]、システムはインデックススキャンを優先します。値を大きくすると、インデックススキャンのコストが比較的高くなります。
より低い値が実際にPostgresがインデックスを使用するかどうかを確認できます(ただし、これはテストのみに使用します):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
SET random_page_cost = DEFAULT;
で再びデフォルト値を復元できます。
インデックススキャンには、非順次ディスクページフェッチが必要です。 Postgresはrandom_page_cost
を使用して、シーケンシャルフェッチに関連するこのような非シーケンシャルフェッチのコストを推定します。デフォルト値は4.0
であるため、averageコストファクターをシーケンシャルフェッチと比較して4(キャッシュ効果を考慮して)と想定しています。
ただし問題は、このデフォルト値が次の重要な実際のシナリオでは不適切であることです。
1)ソリッドステートドライブ
ドキュメントが認めているように:
シーケンシャルに比べてランダム読み取りコストが低いストレージ、例えばソリッドステートドライブは、
random_page_cost
の値を小さくすると、より適切にモデル化される場合があります。
このスライド PostgresConf 2018での講演の最後のポイントによると、random_page_cost
は、ソリッドステートドライブの場合、1.0
と2.0
の間に設定する必要があります。
2)キャッシュされたデータ
必要なインデックスデータが既にRAMにキャッシュされている場合、インデックススキャンは常にシーケンシャルスキャンよりもかなり高速になります。ドキュメントには次のように書かれています:
同様に、データが完全にキャッシュ内にある可能性が高い場合、[...]
random_page_cost
を減らすことが適切です。
問題は、関連するデータが既にキャッシュされているかどうかを簡単に知ることができないことです。ただし、特定のインデックスが頻繁にクエリされ、システムに十分なRAMがある場合、データはキャッシュされる可能性が高いため、random_page_cost
を低い値に設定する必要があります。さまざまな値を試して、何が効果的かを確認する必要があります。
また、明示的なデータキャッシングに pg_prewarm 拡張機能を使用することもできます。
Postgresをプッシュして、サブクエリにOFFSET 0
を追加するseqscanを優先するトリックがあります
これは、必要なのがn個の最初/最後の要素のみである場合に、大きな/巨大なテーブルをリンクするリクエストを最適化するのに便利です。
あなたが探しているものが最初の100または1000にあるときに、10万(またはそれ以上)のエントリを持つ複数のテーブルを含む最初/最後の20要素を探しているとしましょうエントリ。たとえば、このシナリオでは、シーケンシャルスキャンを実行すると10倍以上高速になります。