Dans la réponse connexe à laquelle vous faites référence :
- MISE À JOUR Postgres... LIMITER 1
L'objectif est d'en verrouiller un rangée à la fois. Cela fonctionne bien avec ou sans verrous consultatifs, car il n'y a aucune chance de blocage - tant que vous n'essayez pas de verrouiller plusieurs lignes dans la même transaction.
Votre exemple est différent dans le sens où vous souhaitez verrouiller 3 000 lignes à la fois . Il existe possibilité de blocage, sauf si toutes les opérations d'écriture simultanées verrouillent les lignes dans le même ordre cohérent. Par documentation :
La meilleure défense contre les interblocages est généralement de les éviter en étant certain que toutes les applications utilisant une base de données acquièrent des verrous sur plusieurs objets dans un ordre cohérent.
Implémentez cela avec un ORDER BY dans votre sous-requête.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
C'est sûr et fiable, tant que tous les transactions acquièrent des verrous dans le même ordre et il ne faut pas s'attendre à des mises à jour simultanées des colonnes de classement. (Lisez la case jaune "ATTENTION" à la fin de ce chapitre dans le manuel.) Cela devrait donc être sûr dans votre cas, puisque vous n'allez pas mettre à jour le id
colonne.
En effet, un seul client à la fois peut manipuler les lignes de cette manière. Les transactions simultanées tenteraient de verrouiller les mêmes lignes (verrouillées) et attendraient la fin de la première transaction.
Verrous consultatifs sont utiles si vous avez de nombreuses ou très longues transactions simultanées (il ne semble pas que ce soit le cas). Avec seulement quelques-uns, il sera globalement moins cher d'utiliser simplement la requête ci-dessus et de faire attendre les transactions simultanées.
Tout en une MISE À JOUR
Il semble que l'accès simultané ne soit pas un problème en soi dans votre configuration. La simultanéité est un problème créé par votre solution actuelle.
Au lieu de cela, faites-le tout en un seul UPDATE
. Attribuer des lots de n
nombres (3000 dans l'exemple) à chaque UUID et mettre à jour tous à la fois. Devrait être le plus rapide.
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
Points majeurs
-
La division entière est tronquée. Vous obtenez 1 pour les 3000 premières lignes, 2 pour les 3000 lignes suivantes. etc.
-
Je choisis des lignes arbitrairement, vous pouvez appliquer
ORDER BY
dans la fenêtre pourrow_number()
pour attribuer certaines lignes. -
Si vous n'avez pas de table d'UUID à dispatcher (
uuid_tbl
), utilisez unVALUES
expression pour les fournir. Exemple. -
Vous obtenez des lots de 3000 lignes. Le dernier lot sera inférieur à 3 000 si vous ne trouvez pas de multiple de 3 000 à attribuer.