web-dev-qa-db-ja.com

インデックスはテーブル継承では使用されません

マスターテーブルと2つの子テーブルを持つPostgreSQL 9.0.12データベースがあります。私のテーブル:

_CREATE TABLE test2 (
    id serial PRIMARY KEY,
    coll character varying(15),
    ts timestamp without time zone
);
CREATE INDEX ON test2(ts);

CREATE TABLE test2_20150812 (
    CHECK ( ts >= timestamp '2015-08-12' AND ts < timestamp '2015-08-13' )
) INHERITS (test2);

CREATE TABLE test2_20150811 (
    CHECK ( ts >= timestamp '2015-08-11' AND ts < timestamp '2015-08-12' )
) INHERITS (test2);

CREATE INDEX ON test2_20150812(ts);
CREATE INDEX ON test2_20150811(ts);
VACUUM FULL ANALYZE;
_

私の選択クエリの説明結果(dbには行がありません):

_EXPLAIN (ANALYZE, BUFFERS) select * from test2 WHERE ts >= '2015-08-11' ORDER BY ts DESC;

 Sort  (cost=89.87..92.09 rows=887 width=31) (actual time=0.245..0.245 rows=0 loops=1)
   Sort Key: public.test2.ts
   Sort Method:  quicksort  Memory: 17kB
   Buffers: shared read=2
   ->  Result  (cost=0.00..46.44 rows=887 width=31) (actual time=0.087..0.087 rows=0 loops=1)
         Buffers: shared read=2
         ->  Append  (cost=0.00..46.44 rows=887 width=31) (actual time=0.078..0.078 rows=0 loops=1)
               Buffers: shared read=2
               ->  Seq Scan on test2  (cost=0.00..0.00 rows=1 width=31) (actual time=0.007..0.007 rows=0 loops=1)
                     Filter: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
               ->  Bitmap Heap Scan on test2_20150812 test2  (cost=7.68..23.22 rows=443 width=31) (actual time=0.024..0.024 rows=
0 loops=1)
                     Recheck Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
                     Buffers: shared read=1
                     ->  Bitmap Index Scan on test2_20150812_ts_idx  (cost=0.00..7.57 rows=443 width=0) (actual time=0.016..0.016
 rows=0 loops=1)
                           Index Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
                           Buffers: shared read=1
               ->  Bitmap Heap Scan on test2_20150811 test2  (cost=7.68..23.22 rows=443 width=31) (actual time=0.033..0.033 rows=
0 loops=1)
                     Recheck Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
                     Buffers: shared read=1
                     ->  Bitmap Index Scan on test2_20150811_ts_idx  (cost=0.00..7.57 rows=443 width=0) (actual time=0.026..0.026
 rows=0 loops=1)
                           Index Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
                           Buffers: shared read=1
 Total runtime: 0.320 ms
(23 rows)
_

ただし、列collcharacter varying(15)からcharacter varying(255)に変更し、これらの手順を再度実行すると、

_CREATE TABLE test2 (
    id serial PRIMARY KEY,
    coll character varying(255),
    ts timestamp without time zone
);
_

説明の出力は次のとおりです(dbには行がありません):

_EXPLAIN (ANALYZE, BUFFERS) select * from test2 WHERE ts >= '2015-08-11' ORDER BY ts DESC;

 Sort  (cost=42.47..43.18 rows=287 width=157) (actual time=0.028..0.028 rows=0 loops=1)
   Sort Key: public.test2.ts
   Sort Method:  quicksort  Memory: 17kB
   ->  Result  (cost=0.00..30.75 rows=287 width=157) (actual time=0.020..0.020 rows=0 loops=1)
         ->  Append  (cost=0.00..30.75 rows=287 width=157) (actual time=0.015..0.015 rows=0 loops=1)
               ->  Seq Scan on test2  (cost=0.00..0.00 rows=1 width=157) (actual time=0.003..0.003 rows=0 loops=1)
                     Filter: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
               ->  Seq Scan on test2_20150812 test2  (cost=0.00..15.38 rows=143 width=157) (actual time=0.002..0.002 rows=0 loops
=1)
                     Filter: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
               ->  Seq Scan on test2_20150811 test2  (cost=0.00..15.38 rows=143 width=157) (actual time=0.002..0.002 rows=0 loops
=1)
                     Filter: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)
 Total runtime: 0.063 ms
(12 rows)
_

この新しい条件で子テーブルのインデックスを使用する方法はありますか?

6
umut

これはすべて、継承とパーティショニングとは無関係です。一般的には、インデックス作成とクエリプランについてです。

2回目の試行では、行サイズがはるかに大きくなります:_width=157_と_width=46_。 Postgresは/もっと簡単により広い行のインデックスを使用します。予期しない順次スキャンの考えられる理由は次のとおりです。

  • プランナーの見積もりで示されているように、2番目のテストではテーブルの行が大幅に少なくなっています:_rows=143_と_rows=357_。ソートする数行のみのインデックスを検索するのは無駄です。

  • または、統計が古くなり、誤ったプランナーの見積もりにつながります(Postgresのみthinks行が少なくなります)。

  • インデックスのサイズは、テーブルを書き換える副作用として膨らんだ可能性があります。 REINDEXまたは_VACUUM FULL_はそれを修復します。

関連するすべてのテーブルでANALYZEを実行し、再試行してください-すべてのテーブルで同じ数の行を使用してください。ビットマップインデックススキャンが再び表示されます。現象が続く場合は、EXPLAINだけでなく、EXPLAIN (ANALYZE, BUFFERS)の出力を提供してください。

質問更新後

whole tableを読んでいる限り、インデックスの用途は限られています。一致するインデックスを使用して単一テーブルをクエリし、インデックスから簡単にソートされた行を読み取ることができるようにし、Postgresが/ソート手順を完全にスキップできるようにする場合インデックススキャンが表示されます。

複数のテーブルを組み合わせる必要がある場合、これは不可能です。これは SQLフィドル で、子ごとに1万行があり、有効な統計は、予想どおりビットマップインデックススキャンを示しています。クエリを数回繰り返した後(テーブル全体がキャッシュされるとすぐに)、Postgresはインデックスをスキップして、順次スキャンに切り替える可能性があります。

Postgresは明らかに、相互に除外するチェック制約を理解するのに十分スマートではありません。これにより、各テーブルから簡単にソートされた結果を追加できますas iscould手動で指示することで強制します。

_(SELECT * FROM test2_20150812 ORDER BY ts DESC)
UNION ALL
(SELECT * FROM test2_20150811 ORDER BY ts DESC);
_

ただし、PostgresはMerge Append(事前にソートされたセットを結合するための安価な方法)を使用するのに十分スマートでなければなりません。 PostgreSQLのローカルテストでは、9.4実際に各パーティションでインデックススキャンMerge Appendと組み合わせて表示されます。その計画はより良いですが、whole tableを読んでいる限り、インデックスの用途は限られているため、シーケンシャルスキャンほど高速ではありません。

_'QUERY PLAN'
'Merge Append  (cost=0.73..16866.41 rows=200001 width=45)'
'  Sort Key: test.ts'
'  ->  Index Scan Backward using test_ts_idx on test  (cost=0.13..8.14 rows=1 width=528)'
'        Index Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)'
'  ->  Index Scan Backward using test_20150811_ts_idx on test_20150811  (cost=0.29..6594.01 rows=100000 width=45)'
'        Index Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)'
'  ->  Index Scan Backward using test_20150812_ts_idx on test_20150812  (cost=0.29..6594.29 rows=100000 width=45)'
'        Index Cond: (ts >= '2015-08-11 00:00:00'::timestamp without time zone)'
_

Postgres 9.3(sqlfiddleでのテスト)では同じプランを取得していません。 9.3ページの制限である必要があります。 (?)

ただし、の古いバージョン9.0を使用しているため、それらのどれも使用できません。
Merge Appendは9.1で導入されました

結果を数行に制限すると、より興味深い結果が得られます。 varchar(15)またはvarchar(255)は、クエリプランにほとんど影響を与えません。幅の広いタイプは、インデックスをさらに優先します。

テストクエリを追加したフィドル

SQL Fiddleでのインデックスのテストについて:

4