Ich habe eine Datenbank auf PostgreSQL 9.2, die ein Hauptschema mit ungefähr 70 Tabellen und eine variable Anzahl von identischen Schemata pro Client mit jeweils 30 Tabellen enthält. Die Client-Schemas haben Fremdschlüssel, die auf das Hauptschema verweisen und nicht umgekehrt.
Ich habe gerade angefangen, die Datenbank mit echten Daten aus der Vorgängerversion zu füllen. Die Datenbank hatte ungefähr 1,5 GB erreicht (es wird erwartet, dass sie innerhalb weniger Wochen auf mehrere 10 GB anwächst), als ich in einer sehr zentralen Tabelle im Hauptschema einen Massenlöschvorgang durchführen musste. Alle betroffenen Fremdschlüssel sind mit ON DELETE CASCADE gekennzeichnet.
Es war keine Überraschung, dass dies lange dauern würde, aber nach 12 Stunden wurde klar, dass es mir besser geht, von vorne zu beginnen, die Datenbank zu löschen und die Migration erneut zu starten. Aber was ist, wenn ich diesen Vorgang später wiederholen muss, wenn die Datenbank aktiv und viel größer ist? Gibt es alternative, schnellere Methoden?
Wäre es viel schneller, wenn ich ein Skript schreiben würde, das die abhängigen Tabellen durchsucht, beginnend mit der Tabelle, die am weitesten von der zentralen Tabelle entfernt ist, und die abhängigen Zeilen tabellenweise löscht?
Ein wichtiges Detail ist, dass einige Tabellen Trigger enthalten.
Antworten:
Ich hatte ein ähnliches Problem. Wie sich herausstellte,
ON DELETE CASCADE
verlangsamten diese Auslöser die Geschwindigkeit erheblich, da diese kaskadierten Löschvorgänge furchtbar langsam waren.Ich habe das Problem gelöst, indem ich Indizes für die Fremdschlüsselfelder in den Verweistabellen erstellt habe, und ich habe ein paar Stunden für das Löschen in ein paar Sekunden investiert.
quelle
ON DELETE CASCADE
)EXPLAIN (ANALYZE, BUFFERS)
Abfrage zum Löschen einer einzelnen Zeile durchführen. Dabei sollte angezeigt werden, welche Fremdschlüsseleinschränkungen am längsten gedauert haben (zumindest für mich).PRIMARY
Index ausreicht, aber derUNIQUE
Index ist definitiv nicht gut genug für diesen Zweck.Sie haben ein paar Möglichkeiten. Die beste Option ist das Ausführen eines Batch-Löschvorgangs, damit die Trigger nicht getroffen werden. Deaktivieren Sie die Trigger vor dem Löschen und aktivieren Sie sie dann erneut. Das spart Ihnen sehr viel Zeit. Beispielsweise:
Ein wichtiger Schlüssel hierbei ist, dass Sie die Tiefe der Unterabfragen minimieren möchten. In diesem Fall möchten Sie möglicherweise temporäre Tabellen einrichten, um relevante Informationen zu speichern, damit Sie tiefe Unterabfragen zum Löschen vermeiden können.
quelle
Die einfachste Methode , um das Problem zu lösen , ist ein detailliertes Timing von den PostgreSQL abfragen:
EXPLAIN
. Dazu müssen Sie mindestens eine einzelne Abfrage finden, die vollständig ist, aber länger als erwartet dauert. Nehmen wir an, diese Zeile würde so aussehenAnstatt diesen Befehl wirklich auszuführen, können Sie dies tun
Das Rollback am Ende ermöglicht es, dies auszuführen, ohne die Datenbank wirklich zu ändern, aber Sie erhalten immer noch das detaillierte Timing, was wie viel gekostet hat. Nachdem Sie das ausgeführt haben, stellen Sie möglicherweise in der Ausgabe fest, dass ein Trigger große Verzögerungen verursacht:
Die
time
Angabe erfolgt in ms (Millisekunden), sodass die Überprüfung dieser Einschränkung ca. 12,3 Sekunden dauerte. Sie müssenINDEX
über die erforderlichen Spalten eine neue hinzufügen, damit dieser Trigger effektiv berechnet werden kann. Bei Fremdschlüsselreferenzen muss die Spalte, die auf eine andere Tabelle verweist, indiziert werden (dh die Quellenspalte, nicht die Zielspalte). PostgreSQL erstellt solche Indizes nicht automatisch für Sie undDELETE
ist die einzige häufig verwendete Abfrage, bei der Sie diesen Index wirklich wirklich benötigen. Infolgedessen haben Sie möglicherweise jahrelange Daten gesammelt, bis Sie auf den FallDELETE
stoßen, dass aufgrund des Fehlens eines Indexes zu langsam ist.Wiederholen Sie den Befehl in
begin
/rollback
block, wenn Sie die Leistung dieser Einschränkung behoben haben (oder etwas anderes, das zu lange gedauert hat), damit Sie die neue Ausführungszeit mit der vorherigen vergleichen können. Fahren Sie fort, bis Sie mit der Antwortzeit für das Löschen einzelner Zeilen zufrieden sind (ich habe eine Abfrage von 25,6 Sekunden auf 15 ms, indem ich einfach verschiedene Indizes hinzufüge). Anschließend können Sie den Löschvorgang ohne Hacks abschließen.(Beachten Sie, dass
EXPLAIN
eine Abfrage erforderlich ist, die erfolgreich abgeschlossen werden kann. Ich hatte einmal ein Problem, bei dem PostgreSQL zu langeEXPLAIN
gebraucht hat, um herauszufinden, dass ein Löschvorgang eine Fremdschlüsseleinschränkung verletzt und in diesem Fall nicht verwendet werden kann, da er kein Timing für fehlgeschlagen ausgibt Ich kenne keinen einfachen Weg, um in einem solchen Fall Leistungsprobleme zu beheben.)quelle
Das Deaktivieren von Triggern kann eine Bedrohung für die Datenbankintegrität darstellen und kann nicht empfohlen werden. Wenn Sie jedoch sicher sind, dass Ihre Operation einschränkungssicher ist, können Sie Trigger wie folgt deaktivieren:
SET session_replication_role = replica;
Führen Sie die
DELETE
hier.Führen Sie zum Wiederherstellen von Triggern Folgendes aus:
SET session_replication_role = DEFAULT;
Quelle hier.
quelle
Wenn Sie ON DELETE CASCADE-Trigger haben, sind diese hoffentlich aus einem bestimmten Grund vorhanden und sollten daher nicht deaktiviert werden. Ein weiterer Trick (fügen Sie noch Ihre Indizes hinzu), der für mich funktioniert, besteht darin, eine Löschfunktion zu erstellen, mit der Daten, die mit den Tabellen am Ende der Kaskade beginnen, manuell gelöscht werden und in Richtung der Haupttabelle ausgeführt werden. (Dies ist dasselbe, was Sie tun müssten, wenn Sie einen ON DELETE RESTRICT-Trigger hätten.)
In diesem Fall löschen Sie die Daten in tabelle c, tabelle b und tabelle a
quelle