PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment marquer un certain nombre de lignes dans le tableau lors d'un accès simultané

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 pour row_number() pour attribuer certaines lignes.

  • Si vous n'avez pas de table d'UUID à dispatcher (uuid_tbl ), utilisez un VALUES 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.