Postgresがインデックスを使用するように強制するにはどうすればよいですか?
Postgresがインデックスを使用するように強制するにはどうすればよいですか?
回答:
多くのデータベースに見られる一般的な「インデックスヒント」機能について質問していると仮定すると、PostgreSQLはそのような機能を提供していません。これは、PostgreSQLチームによって行われた意識的な決定でした。その理由と代わりに何ができるかについての良い概要は、ここにあります。その理由は基本的に、PostgreSQLのオプティマイザーが統計に基づいて計画を再評価できるのに対して、データが変更されると後で問題が発生する傾向があるのはパフォーマンスハックであるためです。言い換えれば、今日の優れたクエリプランはおそらく、常に優れたクエリプランになるとは限らず、インデックスヒントは特定のクエリプランを常に強制します。
テストに役立つ非常に鈍いハンマーとして、enable_seqscan
およびenable_indexscan
パラメータを使用できます。見る:
これらは、継続的な本番環境での使用には適していません。クエリプランの選択に問題がある場合は、クエリパフォーマンスの問題を追跡するためのドキュメントを参照してください。enable_
パラメータを設定して離れるだけではいけません。
インデックスを使用する十分な理由がない限り、Postgresが正しい選択をしている可能性があります。どうして?
この古いニュースグループの投稿も参照してください。
おそらく使用する唯一の正当な理由
set enable_seqscan=false
クエリを作成していて、テーブルに大量のデータがあった場合にクエリプランが実際にどうなるかをすばやく確認したい場合です。またはもちろん、データセットが小さすぎるという理由だけでクエリがインデックスを使用していないことをすばやく確認する必要がある場合。
set enable_seqscan=false
、クエリを実行set enable_seqscan=true
し、すぐに実行してpostgresqlを適切な動作に戻します(明らかに、本番
SET SESSION 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ではなく、transactions_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
your_wanted_index
。代わりに、postgresqlエンジンがシーケンス/主キースキャンを実行するようにすることができます。結論-PostgreSqlサーバーのインデックス使用を強制する100%信頼できる方法はありません。
where
条件がなく、2つのテーブルまたは結合されていて、Postgresがインデックスを取得できない場合はどうなりますか。
この問題は通常、インデックススキャンの推定コストが高すぎ、現実を正しく反映していない場合に発生します。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
して平均コスト係数を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拡張を使用することもできます。
質問自体は非常に無効です。強制する(たとえば、enable_seqscan = offを実行する)ことは非常に悪い考えです。より高速になるかどうかを確認することは有用かもしれませんが、プロダクションコードはそのようなトリックを使用すべきではありません。
代わりに、クエリの分析を説明し、それを読んで、PostgreSQLが(あなたの意見では)悪い計画を選択する理由を見つけてください。
ウェブ上には、explain analyze出力の読み取りに役立つツールがあります。そのうちの1つは、私が記述したExplain.depesz.comです。
もう1つのオプションは、freenode ircネットワークの#postgresqlチャネルに参加し、そこで助けを求めるためにそこにいる人たちと話し合うことです-クエリを最適化することは「質問して、満足してもらう」の問題ではないためです。それは会話のようなものであり、チェックすべき多くのこと、学ぶべき多くのことがあります。
OFFSET 0
サブクエリにseqscanを追加することを好むようにpostgresをプッシュするトリックがあります
これは、必要なのが最初/最後のn個の要素のみである場合に、大きなテーブルや巨大なテーブルをリンクするリクエストを最適化するのに便利です。
100k(またはそれ以上)のエントリを持つ複数のテーブルを含む最初/最後の20要素を探しているとします。エントリ。たとえば、このシナリオでは、順次スキャンを実行すると、10倍以上速くなることがわかります。