web-dev-qa-db-fra.com

SELECT FOR UPDATE verrouillant d'autres tables dans PostgreSQL

La situation, avec PostgreSQL 9.6:

  • table A avec clé primaire entière
  • table B avec contrainte de clé étrangère sur sa clé primaire référençant la clé primaire de la table A

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 /*
5
Julien

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 pas SELECT FOR KEY SHARE commandes qui tentent d'acquérir un verrou sur les mêmes lignes. Ce mode de verrouillage est également acquis par tout UPDATE qui n'acquiert pas un FOR 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 ;
7
ypercubeᵀᴹ

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.

0
shubham