Früher oder später stehen viele vor der Notwendigkeit, etwas in den Tabelleneinträgen massiv zu reparieren. Ich habe bereits gesagt, wie man es besser macht und wie - es ist besser, es nicht zu machen. Heute werde ich über den zweiten Aspekt des Massenupdates sprechen - das Auslösen von Triggern .In einer Tabelle, in der Sie etwas reparieren müssen, hängt beispielsweise ein böser Auslöser ON UPDATE
, der alle Änderungen auf einige Aggregate überträgt. Und Sie müssen alles so sorgfältig aktualisieren (z. B. ein neues Feld initialisieren), dass diese Einheiten nicht betroffen sind.Lassen Sie uns einfach die Auslöser ausschalten!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
UPDATE ...;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Eigentlich ist das alles - alles hängt schon .Weil es ALTER TABLE
eine AccessExclusive-Sperre auferlegt , unter der niemand, der parallel läuft, auch nur eine einfache SELECT
, etwas aus der Tabelle lesen kann. Das heißt, bis diese Transaktion abgeschlossen ist, wartet jeder, der „nur lesen“ möchte. Und wir erinnern uns, dass UPDATE
wir do-o-olgi ...Lassen Sie es uns schnell ausschalten und dann schnell einschalten!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;
UPDATE ...;
BEGIN;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Hier ist die Situation besser, die Wartezeit ist viel kürzer. Aber nur zwei Probleme verderben die ganze Schönheit:ALTER TABLE
Er wartet auf alle anderen Operationen auf dem Tisch, auch lange SELECT
- Während der Auslöser ausgeschaltet ist, fliegt jede Änderung in der Tabelle vorbei, nicht einmal unsere. Und nun, es wird nicht in die Einheiten gelangen, obwohl es sollte. Ärger!
Verwaltung von Sitzungsvariablen
In der vorherigen Version sind wir also auf einen entscheidenden Punkt gestoßen - wir müssen den Auslöser irgendwie lehren, um „unsere“ Änderungen in der Tabelle von „nicht unsere“ zu unterscheiden. Überspringen Sie "unsere" wie sie ist, aber "nicht unsere" - Auslöser. Hierfür können Sie Sitzungsvariablen verwenden .session_replication_role
Lesen Sie das Handbuch :Die Triggervariable wird auch von der Konfigurationsvariablen session_replication_role beeinflusst . Trigger, die ohne zusätzliche Anweisungen aktiviert sind (Standard), werden ausgelöst, wenn die Replikationsrolle "Ursprung" (Standard) oder "Lokal" ist. Durch eine Anzeige aktivierte Trigger ENABLE REPLICA
werden nur ausgelöst, wenn der aktuelle Sitzungsmodus "Replikat" ist, und durch eine Anzeige aktivierte Trigger ENABLE ALWAYS
werden unabhängig vom aktuellen Replikationsmodus ausgelöst.
Ich betone, dass die Konfiguration nicht für alle auf einmal gilt ALTER TABLE
, sondern nur für unsere separate spezielle Verbindung. Insgesamt, damit keine Anwendung einen Brand auslöst:SET session_replication_role = replica;
UPDATE ...;
SET session_replication_role = DEFAULT;
Zustand im Abzug
Die obige Option funktioniert jedoch für alle Trigger gleichzeitig (oder Sie müssen Trigger im Voraus "auslösen", die Sie nicht deaktivieren möchten). Und wenn wir einen bestimmten Auslöser "ausschalten" müssen ?Die Sitzungsvariable "Benutzer" hilft uns dabei :Die Namen der Erweiterungsparameter werden wie folgt geschrieben: Erweiterungsname, Punkt und dann der Parametername selbst, wie die vollständigen Namen der Objekte in SQL. Zum Beispiel: plpgsql.variable_conflict.
Da Nicht-Systemparameter in Prozessen festgelegt werden können, in denen das entsprechende Erweiterungsmodul nicht geladen wird, akzeptiert PostgreSQL Werte für beliebige Namen mit zwei Komponenten .
Ändern Sie zunächst den Auslöser wie folgt:BEGIN
IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
IF TG_OP IN ('INSERT', 'UPDATE') THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END IF;
...
Übrigens kann dies CREATE OR REPLACE
für eine Triggerfunktion "profitabel" ohne Sperren durchgeführt werden. Und dann spannen wir in der speziellen Verbindung "unsere" Variable:
SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = '';
Kennst du andere Wege? Teilen Sie in den Kommentaren.