マスターテーブルと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_exclusion
がON
に設定されているpostgresql.conf
。したがって、test_20150812
。
マスターテーブルでクエリを実行すると、インデックスが使用されないことがわかります。どうすれば改善できますか?すべてのクエリをマスターテーブルで実行したいと考えています。特定の日付のクエリを実行するとき、マスターテーブルと子テーブルのクエリでパフォーマンスの違いはないと予想します。
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;
...インデックスを一致させます。
CHECK
制約CHECK
定数は、date
定数の代わりにtimestamp
定数で保存されます。次のようになります:
CHECK (ts >= timestamp '2015-08-11' AND ts < timestamp '2015-08-12');
あなたが書く:
constraint_exclusion
がpostgresql.conf
でON
に設定されています。したがって、test_20150812
でのみ実行する必要があります。
クエリプランで確認できるように、test
とtest_20150812
のみがスキャンされ、test_20150811
はスキャンされません。エルゴ:制約の除外は正常に機能しています。すべての偏差はdespiteです。それはちょうど別の間違ったトラックです。
これをすべてクリーンアップした後、seqスキャンではなく、子テーブルのbitmap index scanが表示されます。子テーブルのみのクエリよりもさらに低速です。これは明らかに、親テーブル自体にも一致する行があり、残りの行と並べ替える必要があるため、結果をインデックスから読み取ることができないためです。