PostgreSQL 9.3では、めったに使用されない(全レコードの0.00001%)ブール列に効率的なインデックスを作成しようとしています。そのために、私はSOでこの投稿を発見しました: https://stackoverflow.com/a/12026593/808921
Erwin Brandstetterが推奨するPostgreSQLの「部分インデックス」機能を利用しようとしています。すでに数百万のレコードを持つテーブルがあり、次のようにそのテーブルにインデックスを追加したいと思います。
CREATE INDEX schema_defs_deprovision ON schema_defs (deprovision)
WHERE deprovision = 0;
(大部分のレコードにはdeprovision = 1
)
問題は、最も単純なクエリでこのインデックスを使用しようとすると、PostgreSQLが存在しないかのように動作することです。
explain select * from schema_defs where deprovision = 0;
Seq Scan on schema_defs (cost=0.00..1.05 rows=1 width=278)
Filter: (deprovision = 0)
本当に奇妙なことは、このインデックスがbeforeの前に作成された場合、テーブルにデータがある場合、実際には正常に機能することがわかりました。信じられない?ここにいくつかのSQL Fiddleエントリがそれを証明しています:
挿入後に作成される部分インデックス (インデックスが機能しない)
挿入前に作成された部分インデックス (インデックスは適切に機能します)
どちらの場合も、[実行プランの表示]リンクを展開して、私が話していることを確認してください。
だから、私の質問は-PostgreSQLに、インデックスが作成される前にデータが含まれていたテーブルで部分インデックスの使用を開始させるには何をしなければならないのですか?
ところで、私はSQLの開発者でもありますFiddleこの質問は、私がSQLのために行っている新しい開発作業に関連しています。
インデックスを追加した後、ANALYZE
を実行します。そして、列deprovision
has統計を確認してください。確認方法は?
pg_class
の基本的な統計:
SELECT relname, relkind, reltuples, relpages
FROM pg_class
WHERE oid = 'schema_defs'::regclass;
pg_stats
(pg_statistics
)の列ごとのデータヒストグラム:
SELECT attname, inherited, n_distinct
, array_to_string(most_common_vals, E'\n') AS most_common_vals
FROM pg_stats
WHERE tablename = 'schema_defs'
AND attname = 'deprovision';
PostgreSQLクエリプランナーは、クエリの適切なプランを生成するために、テーブルの内容に関する統計情報に依存しています。これらの統計は
ANALYZE
コマンドによって収集されます。このコマンドは、単独で、またはVACUUM
のオプションのステップとして呼び出すことができます。適度に正確な統計を取得することが重要です。そうしないと、計画の選択が不十分なためにデータベースのパフォーマンスが低下する可能性があります。自動バキュームデーモンが有効になっている場合、テーブルのコンテンツが十分に変更されると、自動的に
ANALYZE
コマンドが発行されます。ただし、管理者は手動でスケジュールされたANALYZE
操作に依存することを好む可能性があります。特に、テーブルの更新アクティビティが「興味深い」列の統計に影響しないことがわかっている場合は特にそうです。デーモンはANALYZE
を、挿入または更新された行数の関数として厳密にスケジュールします。それが意味のある統計的変化につながるかどうかについては知りません。
あなたのケースでは、1つの列だけを分析することが仕事をします:
ANALYZE table_name (deprovision);
そこにいる間、列deprovision
にインデックスがあることは意味がありません。述語WHERE deprovision = 0
を指定すると、追加の情報は含まれません。定数式を使用することもできます。
CREATE INDEX schema_defs_deprovision ON schema_defs ((true))
WHERE deprovision = 0;
単なる概念実証。これはnotになり、さらに便利です。この特別なケースでは、インデックスcolumnはまったく必要ありませんが、-mustには少なくとも1つの列または式を指定する必要があります。したがって、主キーを使用します(変更されず、インデックスが作成されるため、制限やオーバーヘッドコストが増えることはありません)、またはクエリに役立つ可能性のあるその他の小さな列(8バイト以下)を使用します。
CREATE INDEX schema_defs_deprovision ON schema_defs (id)
WHERE deprovision = 0;
デモのフィドルはmisleadingです。
挿入前に作成された部分インデックス (インデックスは適切に機能します)
デモテーブルには4行しかありません。 Postgresはnotインデックスを使用する必要があります。同様の問題が、正反対です。 Postgresは、作成直後のテーブルに関する統計情報を持ちません-ANALYZE
の最初の実行まで。次に、それはknows行が4つしかなく、インデックスに触れることはありません。
では、なぜ2番目のデモで正しく機能するのでしょうか。マニュアル:
効率上の理由から、
reltuples
とrelpages
はオンザフライで更新されないため、通常は多少古い値が含まれています。これらは、VACUUM
、ANALYZE
、およびいくつかのDDLコマンドCREATE INDEX
などによって更新されます。
大胆な強調鉱山。インデックスを作成する場合after行を挿入すると、pg_class
の基本的な統計が更新されます。ただし、pg_statistic
の詳細な統計ではなく、それらのみです。
pg_statistic
のエントリはANALYZE
およびVACUUM ANALYZE
コマンドによって更新され、新しく更新された場合でも常に概算です。
Postgresが部分インデックスを使用するようにするには(特に、他には何も役に立たない元の形式で)、pg_statistic
のデータヒストグラムも必要です。deprovision = 0
は実際にはまれケースなので、インデックスの使用は有料です。
Autovacuum がこれを処理します。 VACUUM
とANALYZE
を自動的にスケジュールします。ただし、テーブルへの書き込みと次のANALYZE
実行の間に時間枠があります(設定とロードによって異なります)。テーブルの作成またはテーブルへの変更の直後にクエリを実行した場合、それらの最新の変更はまだ統計に反映されていません。それが関連する方法で統計を変更しないかどうか気にしないでください。大きい場合、たとえばINSERT
の後、またはテーブルの作成直後に、手動でANALYZE
を実行して適切なクエリプランを取得します。
temporary tablesは、autovacuumによってまったくカバーされないことに注意してください。必要な場合は、常に手動でANALYZE
を手動で実行する必要があります。
Autovacuumの設定方法や、ANALYZE
を手動で実行するかどうかはわかりません。しかし、私は過去に、sqlfiddleが不足している/古い統計のために誤解を招く可能性があることに気づきました。
ANALYZE
がsqlfiddleの舞台裏でどのように処理されるかに非常に興味があります。特別なことはしない方がいいかもしれませんが、いくつかの情報を歓迎します。おそらく、利用可能なRDBMSバージョンごとに1つの基本的なWebページですか?
SQL Fiddleを作成して、さまざまな統計に対するCREATE INDEX
およびANALYZE
の影響を示しました。
(少なくとも)firstの実行で効果が現れます。後の実行では再現できない場合があるため、新しいスキーマを作成して再度実行する必要があります。
最初に、pg_class
には基本的な統計情報はありません。
relname reltuples relpages
schema_defs 0 0
pg_statistics
のdeprovision
のエントリもまったくありません(結果なし)。
Postgresはテーブルの内容を把握しておらず、デフォルトでインデックスを使用します。これはbadの選択です!
CREATE INDEX
の後、基本的な統計が表示されますが、pg_statistics
にはまだデータヒストグラムがありません。
ANALYZE
の後には両方が表示されます。
適切な統計を使用して、Postgresは順次スキャンを使用するようになりました(インデックスがある場合でも適切な選択です。行が少ない場合はコストが高くなります)。