Ich führe eine Abfrage aus, die beim Hinzufügen von Datensätzen immer langsamer wird. Datensätze werden kontinuierlich über einen automatisierten Prozess hinzugefügt (bash ruft psql auf). Ich möchte diesen Flaschenhals korrigieren. Ich weiß jedoch nicht, was meine beste Option ist.
Dies ist die Ausgabe von pgBadger:
%Vor%Die Tabellenstruktur sieht folgendermaßen aus:
%Vor% Die Tabelle "Parent"
hat eine Eins-zu-viele-Beziehung mit der Tabelle "Foo"
:
Die Tabelle "Foo"
hat eine Eins-zu-viele-Beziehung mit der Tabelle "Bar"
:
Außerdem darf die Tabelle "Bar"
keine überlappenden "Timespan"
-Werte für dieselbe "FooID"
oder "ParentID"
enthalten. Ich habe einen Trigger erstellt, der nach einem beliebigen INSERT
,% co_de ausgelöst wird %, oder UPDATE
, die überlappende Bereiche verhindern.
Der Auslöser enthält einen Abschnitt , der ähnlich aussieht :
%Vor% Die Ergebnisse von DELETE
:
(**** Betonung meiner)
Dies scheint zu zeigen, dass 99% der Arbeit in der EXPLAIN ANALYSE
von JOIN
bis "cte"
(via "Bar"
) ... gemacht wird, aber es verwendet bereits den entsprechenden Index ... es ist immer noch zu langsam.
So lief ich:
%Vor%Ergebnisse:
%Vor% Bietet ein Index dieser Größe (relativ zur Tabelle) viel Leseleistung? Ich dachte über eine sudo-Partition nach, bei der der Index durch mehrere Teilindizes ersetzt wird ... vielleicht hätten die Partialtöne weniger beizubehalten (und zu lesen) und die Leistung würde sich verbessern. Ich habe das noch nie gesehen, nur eine Idee. Wenn dies eine Option ist, kann ich mir keine gute Möglichkeit vorstellen, die Segmente zu begrenzen, da dies auf einem "Foo"
-Wert liegen würde.
Ich denke auch, dass das Hinzufügen von TSTZRANGE
zu "ParentID"
die Dinge beschleunigen würde, aber ich möchte nicht denormalisieren.
Welche anderen Möglichkeiten habe ich?
Bei der Spitzenleistung (Stunde 18:00) fügte der Prozess 14,5 Datensätze pro Sekunde konsistent ... von 1,15 Datensätzen pro Sekunde hinzu.
Dies ist das Ergebnis von:
"Bar"
zur Tabelle "ParentID"
"Bar"
"Foo" ("ParentID", "FooID")
(das Modul btree_gist ist bereits installiert)
Außerdem darf die Tabelle
"Bar"
keine Überschneidung enthalten"Timespan"
Werte für den gleichen"FooID"
oder"ParentID"
. Ich habe einen Trigger erstellt das nach jedemINSERT
,UPDATE
oderDELETE
ausgelöst wird, das verhindert überlappende Bereiche.
Ich schlage vor, dass Sie stattdessen eine Ausschlussbeschränkung verwenden, die viel einfacher, sicherer und schneller ist:
Sie müssen das zusätzliche Modul installieren btree_gist
zuerst. Siehe Anweisungen und Erklärungen in dieser Antwort:
Und Sie müssen "ParentID"
in die Tabelle "Bar"
redundant einfügen, was ein kleiner Preis ist. Tabellendefinitionen könnten wie folgt aussehen:
Ich habe auch den Datentyp für "Bar"."FooID"
von in int8
int4
geändert. Er verweist auf "Foo"."FooID"
, was ein serial
ist, d.h. int4
. Verwenden Sie den passenden Typ int4
(oder nur integer
) aus verschiedenen Gründen, z. B. Performance.
Sie benötigen keinen Trigger mehr (zumindest nicht für diese Aufgabe), und Sie erstellen den Index nicht mehr, da er implizit durch die Ausschlussbedingung erstellt wird . "Bar_FooID_Timerange_idx"
Ein btree-Index auf ("ParentID", "FooID")
wird jedoch höchstwahrscheinlich nützlich sein:
Verwandte:
Ich habe UNIQUE ("ParentID", "FooID")
gewählt und nicht aus einem anderen Grund, da es in beiden Tabellen einen anderen Index mit dem führenden "FooID"
gibt:
Wenn Sie "Bar"."ParentID"
nicht redundant hinzufügen können oder wollen, gibt es einen anderen Rogue Weg - unter der Bedingung, dass "Foo"."ParentID"
nie aktualisiert ist. Stellen Sie das sicher, zum Beispiel mit einem Trigger.
Sie können eine Funktion IMMUTABLE
fälschen:
Ich habe das Schema für den Tabellennamen qualifiziert, um sicherzustellen, dass public
vorausgesetzt wird. Passen Sie sich Ihrem Schema an.
Mehr:
Dann verwenden Sie es in der Ausschlussbedingung:
%Vor% Beim Speichern einer redundanten Spalte int4
wird die Einschränkung teurer und die gesamte Lösung hängt von mehr Vorbedingungen ab.
Sie könnten INSERT
und UPDATE
in eine plpgsql-Funktion einschließen und mögliche Ausnahmen von der Ausschlussbeschränkung ( 23P01 exclusion_violation
) auffangen, um sie irgendwie zu handhaben.
Komplettes Codebeispiel:
In Postgres 9.5 können Sie INSERT
direkt mit der neuen "UPSERT" -Implementierung verarbeiten. Die Dokumentation:
Die optionale
ON CONFLICT
-Klausel gibt eine alternative Aktion an einen eindeutigen Verletzung- oder Ausschlussbedingungsverletzungsfehler auslösen. Für jede einzelne Zeile zum Einfügen vorgeschlagen, entweder die Einfügung oder, wenn ein Arbiter Constraint oder Index von angegeben wirdconflict_target
ist verletzt, die Alternativeconflict_action
ist genommen.ON CONFLICT DO NOTHING
vermeidet einfach das Einfügen einer Zeile als deren alternative Aktion.ON CONFLICT DO UPDATE
aktualisiert die vorhandene Zeile das steht im Widerspruch zu der Zeile, die als alternative Aktion eingefügt werden soll.
Allerdings:
Beachten Sie, dass Ausschlussbedingungen in
ON CONFLICT DO UPDATE
nicht unterstützt werden.
Sie können jedoch weiterhin ON CONFLICT DO NOTHING
verwenden, wodurch mögliche exclusion_violation
-Ausnahmen vermieden werden.Prüfen Sie, ob Zeilen tatsächlich aktualisiert wurden, was günstiger ist:
In diesem Beispiel wird die Überprüfung auf die angegebene Ausschlussbedingung beschränkt. (Ich habe die Einschränkung explizit für diesen Zweck in der obigen Tabellendefinition benannt.) Andere mögliche Ausnahmen werden nicht abgefangen.
Tags und Links sql postgresql database-design postgresql-9.4 postgresql-performance