web-dev-qa-db-ja.com

継承されたテーブルとインデックスのパフォーマンスの問題

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

CREATE TABLE test (
    id serial PRIMARY KEY, 
    date timestamp without time zone
);
CREATE INDEX ON test(date);

私の子テーブル:

CREATE TABLE test_20150812 (
    CHECK ( date >= DATE '2015-08-12' AND date < DATE '2015-08-13' )
) INHERITS (test);

CREATE TABLE test_20150811 (
    CHECK ( date >= DATE '2015-08-11' AND date < DATE '2015-08-12' )
) INHERITS (test);

CREATE INDEX ON test_20150812(date);
CREATE INDEX ON test_20150811(date);

次のようなクエリを実行すると:

select * from test_20150812 where date > '2015-08-12' order by date desc;

非常に速く戻ります(20〜30ミリ秒)。 EXPLAIN出力:

 Limit  (cost=0.00..2.69 rows=50 width=212)
   ->  Index Scan Backward using test_20150812_date_idx on test_20150812  (cost=0.00..149538.92 rows=2782286 width=212)
         Index Cond: (date > '2015-08-12 00:00:00'::timestamp without time zone)

ただし、次のようなクエリを実行した場合:

select * from test where date > '2015-08-12' order by date desc;

時間がかかります(10〜15秒)。 EXPLAIN出力:

 Limit  (cost=196687.06..196687.19 rows=50 width=212)
   ->  Sort  (cost=196687.06..203617.51 rows=2772180 width=212)
         Sort Key: public.test.date
         ->  Result  (cost=0.00..104597.24 rows=2772180 width=212)
               ->  Append  (cost=0.00..104597.24 rows=2772180 width=212)
                     ->  Seq Scan on test  (cost=0.00..0.00 rows=1 width=1857)
                           Filter: (date > '2015-08-12 00:00:00'::timestamp without time zone)
                     ->  Seq Scan on test_20150812 test  (cost=0.00..104597.24 rows=2772179 width=212)
                           Filter: (date > '2015-08-12 00:00:00'::timestamp without time zone)

constraint_exclusionONに設定されているpostgresql.conf。したがって、test_20150812

マスターテーブルでクエリを実行すると、インデックスが使用されないことがわかります。どうすれば改善できますか?すべてのクエリをマスターテーブルで実行したいと考えています。特定の日付のクエリを実行するとき、マスターテーブルと子テーブルのクエリでパフォーマンスの違いはないと予想します。

7
umut

"日付"

timestamp列を呼び出さないでください "日付"、それは非常に誤解を招くものです。さらに良いことに、基本的な型名「日付」を識別子として使用しないでください。エラーが発生しやすく、エラーメッセージがわかりにくくなり、標準のSQLでは 予約済みの単語になります 。次のようになります:

CREATE TABLE test (
  id serial PRIMARY KEY
, ts timestamp NOT NULL  -- also adding NOT NULL constraint
);
CREATE INDEX ON test(ts);

注意事項

制約の除外に関するこの警告に注意してください

制約の除外は、クエリのWHERE句に定数(または外部から提供されたパラメーター)が含まれている場合にのみ機能します。たとえば、CURRENT_TIMESTAMPなどの不変でない関数との比較は最適化できません。プランナは、実行時に関数値がどのパーティションに分類されるかを認識できないためです。

大胆な海抜鉱山。 あなたはこれを避けましたが、混乱する設定では、すぐにつまずくかもしれません。

また、dailyパーティションがあるので:

マスターテーブルのすべてのパーティションに対するすべての制約は、制約の除外中に検査されるため、パーティションの数が多いと、クエリの計画時間が大幅に増加する可能性があります。これらの手法を使用したパーティション分割は、おそらく100パーティションまでうまく機能します。何千ものパーティションを使用しないでください。

大胆な強調鉱山。数か月を超える場合は、代わりに毎週または毎月のパーティションを試してください。

述語の不一致

あなたのチェック条件:

CHECK ( date >= DATE '2015-08-12' AND date < DATE '2015-08-13' )

しかし、クエリには次の条件があります。

where date > '2015-08-12' order by date desc;  -- should be: >=

これにより、わずかな不一致(おそらくが正しくない!)が残り、Postgresに状態の再チェックを強制します。良くないが、あなたの質問に答えることもできない。

>=を使用し、列をNOT NULLにするか、ステートメントに NULLS LAST を追加します。

WHERE ts >= '2015-08-12' ORDER BY ts DESC;

...インデックスを一致させます。

Unclean CHECK制約

CHECK定数は、date定数の代わりにtimestamp定数で保存されます。次のようになります:

CHECK (ts >= timestamp '2015-08-11' AND ts < timestamp '2015-08-12');

制約の除外

あなたが書く:

constraint_exclusionpostgresql.confONに設定されています。したがって、test_20150812でのみ実行する必要があります。

クエリプランで確認できるように、testtest_20150812のみがスキャンされ、test_20150811はスキャンされません。エルゴ:制約の除外は正常に機能しています。すべての偏差はdespiteです。それはちょうど別の間違ったトラックです。

多くの戦いに勝利したが、戦争には勝利しなかった

これをすべてクリーンアップした後、seqスキャンではなく、子テーブルのbitmap index scanが表示されます。子テーブルのみのクエリよりもさらに低速です。これは明らかに、親テーブル自体にも一致する行があり、残りの行と並べ替える必要があるため、結果をインデックスから読み取ることができないためです。

11