PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

So markieren Sie eine bestimmte Anzahl von Zeilen in der Tabelle bei gleichzeitigem Zugriff

In der zugehörigen Antwort beziehen Sie sich auf:

  • Postgres-UPDATE ... LIMIT 1

Ziel ist es, einen zu sperren Reihe auf einmal. Dies funktioniert gut mit oder ohne Advisory Locks, da es keine Chance für einen Deadlock gibt - solange Sie nicht versuchen, weitere Zeilen in derselben Transaktion zu sperren.

Ihr Beispiel unterscheidet sich darin, dass Sie 3000 Zeilen gleichzeitig sperren möchten . Es ist Potenzial für Deadlocks, es sei denn, alle gleichzeitigen Schreibvorgänge sperren Zeilen in derselben konsistenten Reihenfolge. Per Dokumentation:

Die beste Verteidigung gegen Deadlocks besteht im Allgemeinen darin, sie zu vermeiden, indem sichergestellt wird, dass alle Anwendungen, die eine Datenbank verwenden, Sperren für mehrere Objekte in einer konsistenten Reihenfolge erwerben.

Implementieren Sie das mit einem ORDER BY in Ihrer Unterabfrage.

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;

Dies ist sicher und zuverlässig, solange alle Transaktionen erwerben Sperren in derselben Reihenfolge, und gleichzeitige Aktualisierungen der Reihenfolgespalten sind nicht zu erwarten. (Lesen Sie den gelben „VORSICHT“-Kästchen am Ende dieses Kapitels im Handbuch.) Dies sollte also in Ihrem Fall sicher sein, da Sie die id nicht aktualisieren werden Spalte.

Effektiv kann jeweils nur ein Client Zeilen auf diese Weise manipulieren. Gleichzeitige Transaktionen würden versuchen, dieselben (gesperrten) Zeilen zu sperren und warten, bis die erste Transaktion abgeschlossen ist.

Hinweissperren sind nützlich, wenn Sie viele oder sehr lange gleichzeitige Transaktionen haben (scheint nicht zu sein). Bei nur wenigen ist es insgesamt billiger, einfach die obige Abfrage zu verwenden und gleichzeitige Transaktionen warten zu lassen, bis sie an der Reihe sind.

Alles in einem UPDATE

Es scheint, dass der gleichzeitige Zugriff in Ihrem Setup per se kein Problem darstellt. Nebenläufigkeit ist ein Problem, das durch Ihre aktuelle Lösung verursacht wird.

Machen Sie stattdessen alles in einem einzigen UPDATE . Weisen Sie Chargen von n zu Nummern (3000 im Beispiel) zu jeder UUID und aktualisieren Sie alle auf einmal. Sollte am schnellsten sein.

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;

Wichtige Punkte

  • Ganzzahldivision schneidet ab. Sie erhalten 1 für die ersten 3000 Zeilen, 2 für die nächsten 3000 Zeilen. usw.

  • Ich wähle Zeilen willkürlich aus, Sie könnten ORDER BY anwenden im Fenster für row_number() um bestimmte Zeilen zuzuweisen.

  • Wenn Sie keine Tabelle mit zu versendenden UUIDs haben (uuid_tbl ), verwenden Sie einen VALUES Ausdruck, um sie zu liefern. Beispiel.

  • Sie erhalten Stapel von 3000 Zeilen. Der letzte Stapel wird knapp 3000 sein, wenn Sie kein Vielfaches von 3000 finden, das Sie zuweisen können.