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.
quelle
explain (buffers, analyze, verbose) ...
könnte mehr Licht werfen.traffic
. Außerdem: Warum zeigt die zweiteEXPLAIN
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;
Antworten:
Zwei Dinge, die hier sehr seltsam sind:
Die Abfrage wählt 300.000 Zeilen aus einer Tabelle mit mehr als 1 Million Zeilen aus. Für 30% (oder etwas über 5% - abhängig von der Zeilengröße und anderen Faktoren) lohnt es sich normalerweise nicht, überhaupt einen Index zu verwenden. Wir sollten einen sequentiellen Scan sehen .
Die Ausnahme wären reine Index-Scans, die ich hier nicht sehe. Der vorgeschlagene mehrspaltige Index @Craig ist die beste Option, wenn Sie nur Index-Scans erhalten. Bei vielen Updates, wie Sie bereits erwähnt haben, funktioniert dies möglicherweise nicht. In diesem Fall sind Sie ohne die zusätzlichen Spalten und nur den Index, den Sie bereits haben, besser dran. Sie könnten in der Lage sein , machen es mit für Sie arbeiten aggressivere Einstellungen autovacuum für die Tabelle. Sie können Parameter für einzelne Tabellen anpassen.
Während Postgres den Index verwenden wird, würde ich sicherlich einen Bitmap-Index-Scan für so viele Zeilen erwarten , keinen einfachen Index-Scan, der normalerweise die bessere Wahl für einen geringen Prozentsatz von Zeilen ist. Sobald Postgres mehrere Treffer pro Datenseite erwartet (gemessen an den Statistiken in der Tabelle), wechselt das Unternehmen normalerweise zu einem Bitmap-Index-Scan.
Demnach würde ich vermuten, dass Ihre Kosteneinstellungen unzureichend sind (und möglicherweise auch die Tabellenstatistik). Möglicherweise haben Sie relativ zu gesetzt
random_page_cost
und / oder zu niedrig eingestellt . Folgen Sie den Links und lesen Sie das Handbuch.cpu_index_tuple_cost
seq_page_cost
Würde auch zu der Beobachtung passen, dass Cold Cache ein großer Faktor ist, wie wir in Kommentaren herausgearbeitet haben. Entweder greifen Sie auf (Teile von) Tabellen zu, die seit langem niemand mehr berührt hat, oder Sie arbeiten auf einem Testsystem, auf dem der Cache (noch) nicht gefüllt ist?
Andernfalls steht Ihnen einfach nicht genügend RAM zur Verfügung, um die meisten relevanten Daten in Ihrer Datenbank zwischenzuspeichern. Folglich ist der Direktzugriff viel teurer als der sequentielle Zugriff, wenn sich die Daten im Cache befinden. Abhängig von der tatsächlichen Situation müssen Sie möglicherweise Anpassungen vornehmen, um bessere Abfragepläne zu erhalten.
Ein weiterer Faktor muss für eine langsame Reaktion beim ersten Lesevorgang erwähnt werden: Hinweisbits . Lesen Sie Details im Postgres-Wiki und zu dieser verwandten Frage:
Oder die Tabelle ist extrem aufgebläht . In diesem Fall wäre ein Index-Scan sinnvoll und ich würde auf
CLUSTER
/pg_repack
in meiner vorherigen Antwort, die Sie zitiert haben, zurückgreifen . (Oder einfachVACUUM FULL)
und untersuchen Sie IhreVACUUM
Einstellungen. Diese sind wichtig mitmany inserts and updates every day
.UPDATE
BerücksichtigenFILLFACTOR
Sie je nach Muster auch einen Wert unter 100. Wenn Sie meistens nur neu hinzugefügte Zeilen aktualisieren, stellen SieFILLFACTER
nach dem Komprimieren Ihrer Tabelle die niedrigere ein , damit nur neue Seiten Spielraum für Aktualisierungen haben.Schema
Passen Sie die Spaltenfolge leicht an, um 8 Bytes pro Zeile zu sparen (in 99% der Fälle, in denen
campaign_id
NULL ist):Detaillierte Erklärung und Links zu mehr:
Messen:
quelle
Es sieht für mich so aus, als würden Sie viele Daten in einem großen Index abfragen, also ist es langsam. Da ist nichts Besonderes los.
Wenn Sie mit PostgreSQL 9.3 oder 9.4 arbeiten, können Sie versuchen, einen Nur-Index-Scan zu erhalten, indem Sie daraus einen abdeckenden Index machen.
PostgreSQL bietet keine echten Indexe oder keine Unterstützung für Indexbegriffe, die nur Werte sind und nicht Teil des B-Baums sind. Dies ist also langsamer und teurer als bei diesen Funktionen. Es könnte immer noch ein Gewinn für einen einfachen Index-Scan sein, wenn das Vakuum oft genug läuft, um die Sichtbarkeitskarte auf dem neuesten Stand zu halten.
Im Idealfall unterstützt PostgreSQL zusätzliche Datenfelder in einem Index wie in MS-SQL Server ( diese Syntax funktioniert in PostgreSQL NICHT ):
quelle
random_page_cost
usw.) zu spielen. Auch für Prüfzwecke nur sehen , obset enable_indexscan = off
undset enable_seqscan = off
dann wieder laufen Kräfte ein Index-Only - Scan, und wenn ja, was ihre Kostenschätzungen von explain analysieren sind.