HerokuでPostgres 9.3を使用しています。
毎日多くの挿入と更新を行う100万件以上のレコードを含む「トラフィック」テーブルがあります。このテーブル全体でさまざまな時間範囲でSUM操作を実行する必要があります。これらの呼び出しには最大40秒かかる可能性があり、それを改善する方法に関する提案を聞きたいです。
このテーブルには次のインデックスが設定されています。
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
SELECTステートメントの例を次に示します。
SELECT SUM("clicks") AS clicks, SUM("impressions") AS impressions
FROM "traffic"
WHERE "uuid_self" != "uuid_partner"
AND "campaign_id" is NULL
AND "dt_created" >= 'Sun, 29 Mar 2015 00:00:00 +0000'
AND "dt_created" <= 'Mon, 27 Apr 2015 23:59:59 +0000'
そして、これはEXPLAIN ANALYZEです:
Aggregate (cost=21625.91..21625.92 rows=1 width=16) (actual time=41804.754..41804.754 rows=1 loops=1)
-> Index Scan using idx_traffic_partner_only on traffic (cost=0.09..20085.11 rows=308159 width=16) (actual time=1.409..41617.976 rows=302392 loops=1)
Index Cond: ((dt_created >= '2015-03-29'::date) AND (dt_created <= '2015-04-27'::date))
Total runtime: 41804.893 ms
http://explain.depesz.com/s/gGA
この質問はSEの別の質問と非常に似ていますが、1つの質問は2つの列のタイムスタンプ範囲にわたってインデックスを使用し、そのクエリのインデックスプランナーはかなりずれた見積もりを持っていました。主な提案は、並べ替えられた複数列のインデックスを作成することでしたが、単一列のインデックスの場合はあまり効果がありません。他の提案はCLUSTER / pg_repackとGISTインデックスを使用することでしたが、通常のインデックスを使用するより良い解決策があるかどうかを確認したいので、まだ試していません。
参考までに、DBでは使用されていない次のインデックスを試してみました。
INDEX idx_traffic_2 ON traffic (campaign_id, uuid_self, uuid_partner, dt_created);
INDEX idx_traffic_3 ON traffic (dt_created);
INDEX idx_traffic_4 ON traffic (uuid_self);
INDEX idx_traffic_5 ON traffic (uuid_partner);
編集:EXPLAIN(分析、詳細、コスト、バッファ)を実行し、これらは結果でした:
Aggregate (cost=20538.62..20538.62 rows=1 width=8) (actual time=526.778..526.778 rows=1 loops=1)
Output: sum(clicks), sum(impressions)
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
-> Index Scan using idx_traffic_partner_only on public.traffic (cost=0.09..20224.74 rows=313881 width=8) (actual time=0.049..431.501 rows=302405 loops=1)
Output: id, uuid_self, uuid_partner, impressions, clicks, dt_created... (other fields redacted)
Index Cond: ((traffic.dt_created >= '2015-03-29'::date) AND (traffic.dt_created <= '2015-04-27'::date))
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
Total runtime: 526.881 ms
http://explain.depesz.com/s/7Gu6
テーブル定義:
CREATE TABLE traffic (
id serial,
uuid_self uuid not null,
uuid_partner uuid not null,
impressions integer NOT NULL DEFAULT 1,
clicks integer NOT NULL DEFAULT 0,
campaign_id integer,
dt_created DATE DEFAULT CURRENT_DATE NOT NULL,
dt_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
)
idは主キーで、uuid_self、uuid_partner、campaign_idはすべて外部キーです。dt_updatedフィールドはpostgres関数で更新されます。
traffic
。また、2番目EXPLAIN
のグラフで42秒から0.5秒に低下したのはなぜですか。最初の実行はコールドキャッシュでしたか?
id
です。他の制約はありますか?NULLになる可能性のある2つの列が表示されます。それぞれのNULL値のパーセンテージは何ですか?何を手に入れますか?SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
もっと光を当てるかもしれません。