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

Come contrassegnare un certo numero di righe nella tabella in caso di accesso simultaneo

Nella relativa risposta ti riferisci a:

  • AGGIORNAMENTO Postgres... LIMITE 1

L'obiettivo è bloccare uno fila alla volta. Funziona bene con o senza blocchi di avviso, perché non c'è alcuna possibilità di un deadlock - a condizione che non provi a bloccare più righe nella stessa transazione.

Il tuo esempio è diverso in quanto desideri bloccare 3000 righe alla volta . Ecco è potenziale deadlock, tranne se tutte le operazioni di scrittura simultanee bloccano le righe nello stesso ordine coerente. Per documentazione:

La migliore difesa contro i deadlock è generalmente evitarli assicurandosi che tutte le applicazioni che utilizzano un database acquisiscano blocchi su più oggetti in un ordine coerente.

Implementalo con un ORDER BY nella tua sottoquery.

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;

Questo è sicuro e affidabile, purché tutti le transazioni acquisiscono blocchi nello stesso ordine e non sono previsti aggiornamenti simultanei delle colonne di ordinamento. (Leggi il riquadro giallo "ATTENZIONE" alla fine di questo capitolo nel manuale.) Quindi questo dovrebbe essere sicuro nel tuo caso, dal momento che non aggiornerai il id colonna.

In effetti, solo un client alla volta può manipolare le righe in questo modo. Le transazioni simultanee tenteranno di bloccare le stesse righe (bloccate) e attenderanno il completamento della prima transazione.

Blocchi di avviso sono utili se hai molte o transazioni simultanee molto lunghe (non sembra che tu lo faccia). Con solo pochi, sarà nel complesso più economico utilizzare solo la query precedente e fare in modo che le transazioni simultanee attendano il loro turno.

Tutto in un AGGIORNAMENTO

Sembra che l'accesso simultaneo non sia un problema di per sé nella tua configurazione. La concorrenza è un problema creato dalla tua soluzione attuale.

Invece, fallo tutto in un unico UPDATE . Assegna lotti di n numeri (3000 nell'esempio) a ciascun UUID e aggiornali tutti in una volta. Dovrebbe essere il più veloce.

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;

Punti principali

  • La divisione intera viene troncata. Ottieni 1 per le prime 3000 righe, 2 per le successive 3000 righe. ecc.

  • Scelgo le righe in modo arbitrario, potresti applicare ORDER BY nella finestra per row_number() per assegnare determinate righe.

  • Se non hai una tabella di UUID da inviare (uuid_tbl ), usa un VALUES espressione per fornirli. Esempio.

  • Ottieni lotti di 3000 righe. L'ultimo batch sarà inferiore a 3000 se non trovi un multiplo di 3000 da assegnare.