Estou usando o Postgres 9.3 através do Heroku.
Eu tenho uma tabela, "tráfego", com mais de 1 milhão de registros com muitas inserções e atualizações todos os dias. Preciso executar operações SUM nesta tabela em diferentes intervalos de tempo e essas chamadas podem levar até 40 segundos e gostaria de ouvir sugestões sobre como melhorar isso.
Eu tenho o seguinte índice em vigor nesta tabela:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Aqui está um exemplo de instrução 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'
E este é o 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
Essa pergunta é muito semelhante à outra no SE, mas essa usou um índice em dois intervalos de carimbo de data / hora da coluna e o planejador de índice para essa consulta tinha estimativas muito distantes. A principal sugestão foi criar um índice classificado de várias colunas, mas para índices de coluna única que não têm muito efeito. As outras sugestões foram usar os índices CLUSTER / pg_repack e GIST, mas ainda não os experimentei, pois gostaria de ver se há uma solução melhor usando índices regulares.
Otimizando consultas em vários timestamps (duas colunas)
Para referência, tentei os seguintes índices, que não foram usados pelo 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);
EDIT : Ran EXPLAIN (ANALISAR, VERBOSO, CUSTOS, TAMPÕES) e estes foram resultados:
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
Definição da tabela:
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 é a chave primária e uuid_self, uuid_partner e campaign_id são todas chaves estrangeiras. O campo dt_updated é atualizado com uma função postgres.
traffic
. Além disso: por que o segundo EXPLAIN
mostra uma queda de 42 s para 0,5 s? A primeira execução foi realizada com cache frio?
id
? Alguma outra restrição? Eu vejo duas colunas que podem ser NULL. Qual é a porcentagem de valores NULL em cada um? O que você ganha por isso? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
pode lançar mais luz.