Ich benutze Postgres 9.3 über Heroku.
Ich habe eine Tabelle, "Verkehr", mit 1M + Datensätzen, die jeden Tag viele Einfügungen und Aktualisierungen enthält. Ich muss SUM-Operationen in dieser Tabelle über verschiedene Zeitbereiche ausführen. Diese Anrufe können bis zu 40 Sekunden dauern und würden gerne Vorschläge hören, wie dies verbessert werden kann.
Ich habe den folgenden Index für diese Tabelle:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Hier ist ein Beispiel für eine SELECT-Anweisung:
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'
Und das ist die 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
Diese Frage ist einer anderen in SE sehr ähnlich, aber diese verwendete einen Index über zwei Spalten-Zeitstempelbereiche und der Indexplaner für diese Abfrage hatte Schätzungen, die weit entfernt waren. Der Hauptvorschlag bestand darin, einen sortierten mehrspaltigen Index zu erstellen, jedoch für einspaltige Indizes, die keine großen Auswirkungen haben. Die anderen Vorschläge waren die Verwendung von CLUSTER / pg_repack- und GIST-Indizes, aber ich habe sie noch nicht ausprobiert, da ich gerne sehen würde, ob es eine bessere Lösung mit regulären Indizes gibt.
Optimieren von Abfragen für eine Reihe von Zeitstempeln (zwei Spalten)
Als Referenz habe ich die folgenden Indizes ausprobiert, die von der DB nicht verwendet wurden:
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 (ANALYSE, VERBOSE, KOSTEN, PUFFER) und dies waren Ergebnisse:
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
Tabellendefinition:
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 ist der Primärschlüssel und uuid_self, uuid_partner und Campaign_id sind alle Fremdschlüssel. Das Feld dt_updated wird mit einer Postgres-Funktion aktualisiert.
traffic
. Außerdem: Warum zeigt die zweite EXPLAIN
einen Abfall von 42 Sekunden auf 0,5 Sekunden? War der erste Lauf mit kaltem Cache?
id
. Irgendwelche anderen Einschränkungen? Ich sehe zwei Spalten, die NULL sein können. Wie viel Prozent der NULL-Werte sind jeweils enthalten? Was bekommst du dafür? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
könnte mehr Licht werfen.