PostgreSQL Antipatterns: Ändern von Daten unter Umgehung eines Triggers

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 TABLEeine 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 UPDATEwir 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 REPLICAwerden nur ausgelöst, wenn der aktuelle Sitzungsmodus "Replikat" ist, und durch eine Anzeige aktivierte Trigger ENABLE ALWAYSwerden 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 REPLACEfü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.

All Articles