sql >> Database teknologi >  >> RDS >> PostgreSQL

Sådan markeres et bestemt antal rækker i tabellen ved samtidig adgang

I det relaterede svar henviser du til:

  • Postgres OPDATERING ... GRÆNSE 1

Målet er at låse en række ad gangen. Dette fungerer fint med eller uden rådgivende låse, for der er ingen chance for dødvande - så længe du ikke forsøger at låse flere rækker i samme transaktion.

Dit eksempel er anderledes ved, at du vil låse 3000 rækker ad gangen . Der er potentiale for deadlock, undtagen hvis alle samtidige skriveoperationer låser rækker i samme konsistente rækkefølge. Per dokumentation:

Det bedste forsvar mod deadlocks er generelt at undgå dem ved at være sikker på, at alle applikationer, der bruger en database, får låse på flere objekter i en konsistent rækkefølge.

Implementer det med en ORDER BY i din underforespørgsel.

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;

Dette er sikkert og pålideligt, så længe alle transaktioner får låse i samme rækkefølge, og samtidige opdateringer af bestillingskolonnerne kan ikke forventes. (Læs den gule "FORSIGTIG"-boks i slutningen af ​​dette kapitel i manualen.) Så dette burde være sikkert i dit tilfælde, da du ikke kommer til at opdatere id kolonne.

Faktisk kan kun én klient ad gangen manipulere rækker på denne måde. Samtidige transaktioner ville forsøge at låse de samme (låste) rækker og vente på, at den første transaktion er færdig.

Rådgivende låse er nyttige, hvis du har mange eller meget langvarige samtidige transaktioner (det ser det ikke ud til, at du har). Med kun få vil det generelt være billigere blot at bruge ovenstående forespørgsel og lade samtidige transaktioner vente på deres tur.

Alt i én OPDATERING

Det ser ud til, at samtidig adgang ikke er et problem i sig selv i din opsætning. Samtidighed er et problem skabt af din nuværende løsning.

Gør det i stedet alt i en enkelt UPDATE . Tildel batcher af n numre (3000 i eksemplet) til hver UUID og opdatere alle på én gang. Bør være hurtigst.

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;

Vigtige punkter

  • Heltalsdivision afkortes. Du får 1 for de første 3000 rækker, 2 for de næste 3000 rækker. osv.

  • Jeg vælger rækker vilkårligt, du kan anvende ORDER BY i vinduet for row_number() for at tildele bestemte rækker.

  • Hvis du ikke har en tabel over UUID'er at sende (uuid_tbl ), brug en VALUES udtryk for at levere dem. Eksempel.

  • Du får partier på 3000 rækker. Den sidste batch mangler 3000, hvis du ikke finder et multiplum af 3000 at tildele.



  1. Fix:"den førende præcision af intervallet er for lille" i Oracle Database

  2. Skinner:Installation af PG gem på OS X - fejl i at bygge indbygget udvidelse

  3. Kan jeg fortryde en transaktion, jeg allerede har begået? (tab af data)

  4. UNION ALLE Optimering