Estoy usando Postgres 9.3 a través de Heroku.
Tengo una tabla, "tráfico", con 1M + registros que tiene muchas inserciones y actualizaciones todos los días. Necesito realizar operaciones SUM en esta tabla en diferentes intervalos de tiempo y esas llamadas pueden tomar hasta 40 segundos y me encantaría escuchar sugerencias sobre cómo mejorar eso.
Tengo el siguiente índice en su lugar en esta tabla:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Aquí hay un ejemplo de declaración 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'
Y este es el EXPLICAR ANÁLISIS:
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
Esta pregunta es muy similar a otra en SE, pero esa utilizaba un índice en dos intervalos de marcas de tiempo de columna y el planificador de índice para esa consulta tenía estimaciones que estaban muy lejos. La sugerencia principal fue crear un índice ordenado de varias columnas, pero para los índices de una sola columna eso no tiene mucho efecto. Las otras sugerencias fueron usar los índices CLUSTER / pg_repack y GIST, pero aún no los he probado, ya que me gustaría ver si hay una mejor solución usando índices regulares.
Optimización de consultas en un rango de marcas de tiempo (dos columnas)
Como referencia, probé los siguientes índices, que no fueron utilizados por el 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);
EDITAR : Corrió EXPLICAR (ANALIZAR, VERBOSO, COSTOS, BUFFERS) y estos fueron los 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
Definición de tabla:
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 es la clave principal y uuid_self, uuid_partner y campaign_id son claves foráneas. El campo dt_updated se actualiza con una función postgres.
traffic
. Además: ¿por qué el segundo EXPLAIN
muestra una caída de 42 segundos a 0,5 segundos? ¿Fue la primera ejecución con caché frío?
id
? ¿Alguna otra restricción? Veo dos columnas que pueden ser NULL. ¿Cuál es el porcentaje de valores NULL en cada uno? ¿Qué obtienes por esto? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
podría arrojar más luz.