PostgreSQL-antimønstre: skift data uden om en trigger

Før eller siden står mange over for behovet for massivt at rette noget i tabelregistrene. Jeg har allerede fortalte mig, hvordan man gør det bedre, og hvordan - det er bedre ikke at gøre det. I dag vil jeg tale om det andet aspekt af masseopdateringen - om triggere.

For eksempel, på et bord, hvor du skal ordne noget, hænger en ond udløser ON UPDATE, der overfører alle ændringer til nogle aggregater. Og du skal opdatere alt (f.eks. initialisere et nyt felt) så omhyggeligt, at disse enheder ikke påvirkes.

Lad os bare slå triggere fra!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- тут долго-долго
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Faktisk er det alt - alt hænger.

Fordi ALTER TABLE pålægger Eksklusiv adgang- en lås, hvorunder ingen løber parallelt, heller ikke en simpel SELECT, vil ikke kunne læse noget fra tabellen. Det vil sige, indtil denne transaktion slutter, vil alle, der ønsker at "bare læse" vente. Og det husker vi UPDATE vi har en lang...

Lad os hurtigt slukke for det, og så hurtigt tænde det!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;

UPDATE ...;

BEGIN;
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Her er situationen allerede bedre, ventetiden er væsentligt mindre. Men kun to problemer ødelægger al skønheden:

  • ALTER TABLE selv venter på alle andre operationer på bordet, også lange SELECT
  • Mens aftrækkeren er slukket, enhver ændring vil "flyve forbi" i tabellen, ikke engang vores. Og det vil ikke komme ind i enhederne, selvom det burde. Problemer!

Håndtering af sessionsvariabler

Så i den tidligere version faldt vi over et grundlæggende punkt - vi skal på en eller anden måde lære triggeren at skelne "vores" ændringer i tabellen fra "ikke vores". "Vores" springes over, som den er, og "ikke vores" udløses. Til dette kan du bruge sessionsvariabler.

session_replikeringsrolle

Læs brugervejledning:

Udløsermekanismen påvirkes også af konfigurationsvariablen session_replikeringsrolle. Aktiveret uden yderligere instruktioner (standard), udløses udløsere, når replikeringsrollen er "oprindelse" (standard) eller "lokal". Udløsere aktiveret ved at specificere ENABLE REPLICA, virker kun hvis aktuelle sessionstilstand - "replika", og udløsere aktiveret ved at specificere ENABLE ALWAYS, vil blive udløst uanset den aktuelle replikeringstilstand.

Jeg vil især understrege, at indstillingen ikke gælder for alt-alt på én gang, som ALTER TABLE, men kun til vores særskilte særlige forbindelse. I alt, så ingen applikationsudløser virker:

SET session_replication_role = replica; -- выключили триггеры
UPDATE ...;
SET session_replication_role = DEFAULT; -- вернули в исходное состояние

Tilstand indvendig aftrækker

Men ovenstående mulighed virker for alle triggere på én gang (eller du skal "alternere" triggere på forhånd, som du ikke vil deaktivere). Og hvis vi har brug for det "sluk" en bestemt trigger?

Dette vil hjælpe os "bruger" sessionsvariabel:

Udvidelsesparameternavne skrives som følger: udvidelsesnavnet efterfulgt af en prik og derefter selve parameternavnet, svarende til fulde objektnavne i SQL. For eksempel: plpgsql.variable_conflict.
Fordi indstillinger uden for systemet kan indstilles i processer, der ikke indlæser det relevante udvidelsesmodul, accepterer PostgreSQL værdier for alle navne med to komponenter.

Først ændrer vi triggeren, noget som dette:

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;
...

Dette kan i øvrigt gøres "live", uden at blokere, igennem CREATE OR REPLACE for triggerfunktionen. Og så i den specielle forbindelse vipper vi "vores" variabel:


SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- вернули в исходное состояние

Kender du andre måder? Del i kommentarerne.

Kilde: www.habr.com

Tilføj en kommentar