Sto usando Postgres 9.3 tramite Heroku.
Ho una tabella, "traffico", con record 1M + che ha molti inserti e aggiornamenti ogni giorno. Devo eseguire le operazioni SUM su questa tabella in diversi intervalli di tempo e tali chiamate possono richiedere fino a 40 secondi e mi piacerebbe ricevere suggerimenti su come migliorarlo.
Ho il seguente indice in atto su questa tabella:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Ecco un'istruzione SELECT di esempio:
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 questo è l'ANALISI DI SPIEGAZIONE:
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
Questa domanda è molto simile a un'altra su SE, ma quella utilizzava un indice su due intervalli di data / ora di colonna e il pianificatore di indice per quella query aveva delle stime molto lontane. Il suggerimento principale era quello di creare un indice multi-colonna ordinato, ma per gli indici a colonna singola che non ha molto effetto. Gli altri suggerimenti erano di utilizzare gli indici CLUSTER / pg_repack e GIST, ma non li ho ancora provati, poiché mi piacerebbe vedere se esiste una soluzione migliore utilizzando gli indici regolari.
Ottimizzazione delle query su un intervallo di timestamp (due colonne)
Per riferimento, ho provato i seguenti indici, che non sono stati utilizzati dal 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 (ANALYZE, VERBOSE, COSTI, BUFFER) e questi erano risultati:
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
Definizione della tabella:
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 è la chiave primaria e uuid_self, uuid_partner e campaign_id sono tutte chiavi esterne. Il campo dt_updated viene aggiornato con una funzione postgres.
traffic
. Inoltre: perché il secondo EXPLAIN
mostra un calo da 42 secondi a 0,5 secondi? La prima è stata eseguita con cold cache?
id
? Qualche altro vincolo? Vedo due colonne che possono essere NULL. Qual è la percentuale di valori NULL in ciascuno? Cosa ottieni per questo? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
potrebbe fare più luce.