La situation, avec PostgreSQL 9.6:
SELECT id FROM A FOR UPDATE;
bloque UPDATE B SET x=y;
jusqu'à ce que le verrou de A soit libéré.
Je suppose que c'est parce que la valeur référencée peut changer. Que puis-je faire pour éviter que la première instruction ne bloque l'exécution de la seconde sans supprimer la contrainte de clé étrangère?
Si je supprime la contrainte de clé étrangère, à quoi dois-je m'attendre? Dans la base de données réelle où ce problème se produit, ce ne serait pas un problème important si la table B avait des lignes restantes après la suppression de A. Nous ne mettons également jamais à jour les clés primaires.
Modifier avec le code:
-- Setup
create table a (id int primary key);
create table b (id int primary key references a (id) on update cascade on delete cascade, x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);
-- Left
begin;
select 1 from a for update;
/* Keep the transaction open */
-- Right
begin;
update b set x = 'abc' where id = 1;
update b set x = 'xyz'; /* It blocks here /*
Nous ne mettons également jamais à jour les clés primaires.
Dans ce cas, je pense que vous pouvez utiliser FOR NO KEY UPDATE
au lieu de FOR UPDATE
. C'est un verrou plus faible, comme expliqué dans les documents Postgres sur Verrouillage explicite :
FOR NO KEY UPDATE
Se comporte de la même manière que
FOR UPDATE
, sauf que le verrou acquis est plus faible: ce verrou ne bloquera pasSELECT FOR KEY SHARE
commandes qui tentent d'acquérir un verrou sur les mêmes lignes. Ce mode de verrouillage est également acquis par toutUPDATE
qui n'acquiert pas unFOR UPDATE
fermer à clé.
Tester:
-- Setup
create table a (id int primary key,
x varchar);
create table b (id int primary key
references a (id) on update cascade on delete cascade,
x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);
-- Left
begin;
select 1 from a for no key update;
/* Keep the transaction open */
-- Right
begin;
update b set x = 'abc' where id = 1;
update b set x = 'xyz'; -- doesn't block
-- Left
update a set x = 'left' where id = 1;
commit ;
-- Right
commit ;
FOR UPDATE provoque le verrouillage des lignes récupérées par l'instruction SELECT comme pour la mise à jour. Cela les empêche d'être verrouillés, modifiés ou supprimés par d'autres transactions jusqu'à la fin de la transaction en cours. En d'autres termes, les autres transactions qui tentent la MISE À JOUR de ces lignes seront bloquées jusqu'à la fin de la transaction en cours.
Donc, retirez simplement le verrou en supprimant FOR UPDATE..Je n'ai pas pu comprendre pourquoi vous avez besoin du "FOR UPDATE" sur la première instruction.