web-dev-qa-db-ja.com

PostgreSQLはカウント中にインデックスを使用しません(*)

私は頻繁に実行されるPostgreSQLにCOUNT(*)クエリがあり、次のようになります。

SELECT COUNT(*) 
  FROM customer 
 WHERE source_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

このクエリは実行に30〜60秒かかり、数百万のレコードを検索します。

EXPLAIN ANALYZEは、シーケンシャルスキャンを実行していることを示しているため、インデックスを作成しました。

CREATE INDEX customer_by_source ON customer (source_id)
WHERE source_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

しかしながら、 EXPLAIN ANALYZEは、引き続きシーケンシャルスキャンを実行しており、インデックスを使用していないことを示しています。

このクエリを高速化してインデックスを使用するにはどうすればよいですか?

編集:私のPostgresバージョンは9.3.3です。テーブルには約2,000万のレコードがあり、各source_id間でかなり均等に分割されています。そのうちの5つはリストに含まれていません。

5
Cerin

追加された情報を正しく解釈すると、21の異なる_source_id_があり、それぞれ約100万行(_divided pretty evenly among each source_id_)があります。つまり、クエリはテーブル全体の約3/4をカウントします。通常、インデックスはどちらの方法でもあまり購入できません。

EXPLAINを使用してテストし(この目的にはEXPLAINで十分です)、各バリアントの推定コストを確認します。

_EXPLAIN SELECT ...._-クエリ

そして、出力のすべての_cost=_番号をメモします。

次に(セッションでのみ):

_SET enable_seqscan = off;
_

そして、手順を繰り返します。もう一度後:

_SET enable_indexscan = off;
_

これにより、Postgresが順次スキャンを選択する理由がわかります。通常、インデックスがテーブルよりも大幅に小さい場合(インデックスエントリはテーブルの行よりも大幅に小さいか、行が大幅に少ない場合)、および index-only scan が満たされた場合、Postgresはそのルートを選択します-コスト設定が設定の現実と真剣に接触していない場合を除きます。

最も重要なことは、可視性マップは、ページ全体がすべてのトランザクションから見えることを示す必要があります。 VACUUMは、書き込み後に可視性マップを更新します。リンクされているWikiページの詳細をお読みください。

_VACUUM ANALYZE customer;_を実行した直後にもう一度クエリを試すことができます。

インデックスのみのスキャンが不可能な場合、シーケンシャルスキャンはビットマップインデックススキャンよりも高速である可能性が高く、インデックスは役に立ちません。

Postgres 9.6のアップデート

Postgres 9.6で改善された部分インデックスに重要な制限がありました。 リリースノート:

  • インデックスのWHERE句がインデックス付けされていないカラムを参照する場合、部分インデックスで index-only scan の使用を許可します(Tomas Vondra、Kyotaro Horiguchi)

たとえば、CREATE INDEX tidx_partial ON t(b) WHERE a > 0で定義されたインデックスは、_WHERE a > 0_を指定し、それ以外ではaを使用しないクエリによるインデックスのみのスキャンに使用できるようになりました。以前は、aがインデックス列としてリストされていないため、これは許可されていませんでした。

5