J'ai une clé étrangère existante qui a ON DELETE NO ACTION
défini. Je dois changer cette clé étrangère en ON DELETE CASCADE
. Je peux le faire dans une transaction:
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;
Le problème est que la table posts
est grande (4 millions de lignes), ce qui signifie que la validation de la clé étrangère peut prendre un temps non négligeable (je l'ai testé avec une copie de la base de données). Suppression/ajout de la clé étrangère acquiert un ACCESS EXCLUSIVE
lock sur posts
. Ainsi, l'ajout des blocs de clés étrangères tous donne accès à la table posts
pendant un temps décent car le verrou est maintenu pendant la validation des contraintes. Je dois effectuer une migration en ligne (je n'ai pas de fenêtre d'indisponibilité dédiée).
Je sais que je peux effectuer 2 transactions pour aider le contrôle à prendre longtemps:
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;
L'avantage de cette approche est que le ACCESS EXCLUSIVE
le verrou est maintenu pendant un temps très court pour supprimer/ajouter la contrainte, puis pour valider la contrainte uniquement a SHARE UPDATE EXCLUSIVE
sur posts
et ROW SHARE
verrouiller blogs
puisque je suis sur Postgres 9.5.
Y a-t-il des inconvénients à cela? Je sais que l'ajout de NOT VALID
aux contraintes signifie que existant les données ne sont pas validées, mais toutes les lignes insérées/mises à jour avant le VALIDATE CONSTRAINT
sera à vérifier. Parce que la clé étrangère est supprimée/ajoutée dans la même transaction, existe-t-il une possibilité de créer des données incohérentes?
Les docs le disent à propos de NOT VALID
ADD table_constraint [ NOT VALID ]
Ce formulaire ajoute une nouvelle contrainte à une table en utilisant la même syntaxe que
CREATE TABLE
, plus l'optionNOT VALID
, qui n'est actuellement autorisé que pour les contraintes de clé étrangère et CHECK. Si la contrainte est marquéeNOT VALID
, la vérification initiale potentiellement longue pour vérifier que toutes les lignes de la table satisfont à la contrainte est ignorée. La contrainte sera toujours appliquée contre les insertions ou mises à jour ultérieures (c'est-à-dire qu'elles échoueront à moins qu'il n'y ait une ligne correspondante dans la table référencée, dans le cas des clés étrangères; et elles échoueront à moins que la nouvelle ligne corresponde à la vérification spécifiée contraintes). Mais la base de données ne supposera pas que la contrainte est valable pour toutes les lignes de la table, jusqu'à ce qu'elle soit validée à l'aide deVALIDATE CONSTRAINT
option.
Votre problème,
Je sais que l'ajout de
NOT VALID
aux contraintes signifie que les données existantes ne sont pas validées, mais toutes les lignes insérées/mises à jour avant leVALIDATE CONSTRAINT
sera vérifié.
Ils ne seront vérifiés que lors de la validation [~ # ~] après [~ # ~] vous lui demandez de valider, vous pouvez donc retarder cela jusqu'à ce que vous ayez programmé un temps d'arrêt.
Parce que la clé étrangère est supprimée/ajoutée dans la même transaction, existe-t-il une possibilité de créer des données incohérentes?
Non, car à la seconde où vous ajoutez le NOT VALID
il s'applique à toutes les lignes insérées [~ # ~] après [~ # ~] l'instruction comme si elles étaient toujours là. VALIDATION concerne le refus de créer un FOREIGN KEY
lorsque les lignes référencées n'existent pas. Cela a rien à voir avec la cascade, observez
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)
À ce stade, vous pouvez voir
bar
en cascade vers bar
La contrainte ne peut toujours pas être validée (comme illustré ci-dessous), mais aux fins de la suppression en cascade, tout est bon.
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".
Btw vous pouvez écrire ceci
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;
Comme ça
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;
Vous n'avez pas besoin de l'envelopper dans un txn. Vous n'avez pas non plus à encapsuler une seule instruction dans un txn - PostgreSQL n'est pas MySQL. Tout est déjà transactionnel.