Dina alternativ är:
-
Kör i
SERIALIZABLE
isolering. Interberoende transaktioner kommer att avbrytas vid commit eftersom de har ett serialiseringsfel. Du kommer att få massor av skräppost i felloggar och du kommer att göra många försök igen, men det kommer att fungera tillförlitligt. -
Definiera en
UNIQUE
begränsning och försök igen vid misslyckande, som du noterade. Samma problem som ovan. -
Om det finns ett överordnat objekt kan du
SELECT ... FOR UPDATE
det överordnade objektet innan du gör dittmax
fråga. I det här fallet skulle duSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE
. Du använderbar
som ett lås för allafoo
s med detbar_id
. Du kan då veta att det är säkert att fortsätta, så länge som varje fråga som gör din räknarökning gör detta på ett tillförlitligt sätt. Detta kan fungera ganska bra.Detta gör fortfarande en samlad fråga för varje samtal, vilket (per nästa alternativ) är onödigt, men det spammar åtminstone inte felloggen som ovanstående alternativ.
-
Använd ett bänkbord. Det här är vad jag skulle göra. Antingen i
bar
, eller i en sidotabell sombar_foo_counter
skaffa ett rad-ID medUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counter
eller det mindre effektiva alternativet om ditt ramverk inte kan hantera
RETURNING
:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;
Sedan i samma transaktion , använd den genererade räknarraden för
number
. När du commit, räknartabellraden för denbar_id
låses upp för nästa fråga att använda. Om du rullar tillbaka kasseras ändringen.
Jag rekommenderar att räknaren använder en dedikerad sidotabell för räknaren istället för att lägga till en kolumn i bar
. Det är renare att modellera och innebär att du skapar mindre uppdateringsuppsvällning i bar
, vilket kan sakta ner frågor till bar
.