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 perrow_number()
per assegnare determinate righe. -
Se non hai una tabella di UUID da inviare (
uuid_tbl
), usa unVALUES
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.