J'utilise Postgres 9.3 via Heroku.
J'ai une table, "trafic", avec 1M + d'enregistrements qui a de nombreux insertions et mises à jour chaque jour. J'ai besoin d'effectuer des opérations SUM sur cette table sur différentes plages de temps et ces appels peuvent prendre jusqu'à 40 secondes et j'aimerais entendre des suggestions sur la façon d'améliorer cela.
J'ai l'index suivant en place sur cette table:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Voici un exemple d'instruction 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'
Et voici l'ANALYSE EXPLICITE:
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
Cette question est très similaire à une autre sur SE, mais celle-ci a utilisé un index sur deux plages d'horodatage de colonne et le planificateur d'index pour cette requête avait des estimations très éloignées. La principale suggestion était de créer un index multi-colonnes trié, mais pour les index à colonne unique, cela n'a pas beaucoup d'effet. Les autres suggestions étaient d'utiliser les index CLUSTER / pg_repack et GIST, mais je ne les ai pas encore essayés, car j'aimerais voir s'il existe une meilleure solution en utilisant des index réguliers.
Optimisation des requêtes sur une plage d'horodatages (deux colonnes)
Pour référence, j'ai essayé les index suivants, qui n'étaient pas utilisés par la base de données:
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 (ANALYSER, VERBOSE, COÛTS, TAMPONS) et voici les résultats:
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
Définition du tableau:
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 est la clé primaire et uuid_self, uuid_partner et campaign_id sont toutes des clés étrangères. Le champ dt_updated est mis à jour avec une fonction postgres.
traffic
. Aussi: pourquoi la seconde EXPLAIN
montre-t-elle une baisse de 42 sec à 0,5 sec? La première exécution a-t-elle été effectuée avec un cache froid?
id
? D'autres contraintes? Je vois deux colonnes qui peuvent être NULL. Quel est le pourcentage de valeurs NULL dans chacune? Qu'obtenez-vous pour cela? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
pourrait apporter plus de lumière.