it-swarm.com.de

Ändern des Fremdschlüssels in ON DELETE CASCADE mit geringster Auswirkung

Ich habe einen vorhandenen Fremdschlüssel, für den ON DELETE NO ACTION Definiert ist. Ich muss diesen Fremdschlüssel in ON DELETE CASCADE Ändern. Ich kann dies innerhalb einer Transaktion tun:

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade;
commit;

Das Problem ist, dass die Tabelle posts groß ist (4 Millionen Zeilen), was bedeutet, dass die Überprüfung des Fremdschlüssels eine nicht triviale Zeit in Anspruch nehmen kann (ich habe dies mit einer Kopie der Datenbank getestet). Löschen/Hinzufügen des Fremdschlüssels erhält eine ACCESS EXCLUSIVE - Sperre auf posts. Hinzufügen der Fremdschlüsselblöcke all Zugriff auf die Tabelle posts für einen angemessenen Zeitraum, da die Sperre gehalten wird, während die Überprüfung der Einschränkungen erfolgt. Ich muss eine Online-Migration durchführen (ich habe kein dediziertes Ausfallzeitfenster).

Ich weiß, dass ich 2 Transaktionen ausführen kann, um zu helfen, dass die Prüfung lange dauert:

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade not valid;
commit;

begin;
alter table posts validate constraint posts;
commit;

Der Vorteil dieses Ansatzes besteht darin, dass die Sperre ACCESS EXCLUSIVE Für eine sehr kurze Zeit gehalten wird, um die Einschränkung zu löschen/hinzuzufügen und dann nur die Einschränkung zu validieren a SHARE UPDATE EXCLUSIVE Für posts und ROW SHARE sperren blogs , da ich auf Postgres 9.5 bin.

Gibt es Nachteile? Ich weiß, dass das Hinzufügen von NOT VALID Zu den Einschränkungen bedeutet, dass vorhanden Daten nicht validiert werden, aber alle Zeilen, die vor dem VALIDATE CONSTRAINT eingefügt/aktualisiert werden überprüft werden. Gibt es eine Möglichkeit, inkonsistente Daten zu erstellen, da der Fremdschlüssel in derselben Transaktion gelöscht/hinzugefügt wird?

4
TheCloudlessSky

Die Dokumente sagen dies über NOT VALID

ADD table_constraint [ NOT VALID ]

Dieses Formular fügt einer Tabelle eine neue Einschränkung hinzu, die dieselbe Syntax wie CREATE TABLE sowie die Option NOT VALID Verwendet, die derzeit nur für Fremdschlüssel und CHECK zulässig ist Einschränkungen. Wenn die Einschränkung mit NOT VALID Markiert ist, wird die möglicherweise langwierige Erstprüfung, um sicherzustellen, dass alle Zeilen in der Tabelle die Einschränkung erfüllen, übersprungen. Die Einschränkung wird weiterhin für nachfolgende Einfügungen oder Aktualisierungen erzwungen (dh, sie schlagen fehl, es sei denn, die referenzierte Tabelle enthält eine übereinstimmende Zeile, bei Fremdschlüsseln, und sie schlagen fehl, es sei denn, die neue Zeile entspricht der angegebenen Prüfung Einschränkungen). Die Datenbank geht jedoch nicht davon aus, dass die Einschränkung für alle Zeilen in der Tabelle gilt, bis sie mit der Option VALIDATE CONSTRAINT Validiert wird.

Ihr Anliegen,

Ich weiß, dass das Hinzufügen von NOT VALID Zu den Einschränkungen bedeutet, dass vorhandene Daten nicht validiert werden, aber alle Zeilen, die vor dem VALIDATE CONSTRAINT Eingefügt/aktualisiert wurden, werden überprüft.

Sie werden erst während der Validierung überprüft [~ # ~] nach [~ # ~] Sie weisen die Validierung an, sodass Sie dies verzögern können, bis Sie geplante Ausfallzeiten haben.

Gibt es eine Möglichkeit, inkonsistente Daten zu erstellen, da der Fremdschlüssel in derselben Transaktion gelöscht/hinzugefügt wird?

Nein, denn in der Sekunde, in der Sie NOT VALID Hinzufügen, gilt dies für alle Zeilen, die [~ # ~] nach [~ # ~] der Anweisung eingefügt werden, als ob sie immer da wären. VALIDATION dient zur Ablehnung der Erstellung eines FOREIGN KEY, Wenn die referenzierten Zeilen nicht vorhanden sind. Es hat nichts mit Kaskadierung zu tun, beobachte

CREATE TABLE foo
AS
  SELECT 1 AS a;

CREATE TABLE bar
AS
  SELECT a
  FROM ( VALUES (1),(2) )
    AS t(a);

ALTER TABLE foo
  ADD PRIMARY KEY (a);

ALTER TABLE bar
  ADD FOREIGN KEY (a)
  REFERENCES foo
  ON DELETE CASCADE
  NOT VALID;

DELETE FROM foo;


TABLE foo;
 a 
---
(0 rows)

test=# TABLE bar;
 a 
---
 2
(1 row)

An dieser Stelle können Sie sehen

  1. das Löschen von bar kaskadierte zu bar
  2. das Fehlen einer Validierung in der Leiste bedeutet, dass es immer noch eine Zeile gibt, die ungültig ist

Die Einschränkung kann immer noch nicht validiert werden (wie unten gezeigt), aber zum Zwecke des kaskadierten Löschens sind alle Dinge gut.

ALTER TABLE bar VALIDATE CONSTRAINT bar_a_fkey  ;
ERROR:  insert or update on table "bar" violates foreign key constraint "bar_a_fkey"
DETAIL:  Key (a)=(2) is not present in table "foo".

Übrigens können Sie dies schreiben

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade not valid;
commit;

So was

alter table posts
  drop constraint posts_blog_id_fkey,
  add constraint posts_blog_id_fkey
    foreign key (blog_id)
    references blogs (id)
    on update no action
    on delete cascade
    not valid;

Sie müssen es nicht in einen TXN einwickeln. Sie müssen auch keine einzelne Anweisung in ein txn einschließen - PostgreSQL ist nicht MySQL. Alles ist bereits transaktional.

3
Evan Carroll