Dine muligheder er:
-
Kør i
SERIALIZABLE
isolation. Indbyrdes afhængige transaktioner vil blive afbrudt ved commit som havende en serialiseringsfejl. Du vil få masser af fejllog-spam, og du vil gøre mange genforsøg, men det vil fungere pålideligt. -
Definer en
UNIQUE
begrænsning og prøv igen ved fejl, som du bemærkede. Samme problemer som ovenfor. -
Hvis der er et overordnet objekt, kan du
SELECT ... FOR UPDATE
det overordnede objekt, før du udfører ditmax
forespørgsel. I dette tilfælde skal duSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE
. Du brugerbar
som en lås til allefoo
s med detbar_id
. Du kan så vide, at det er sikkert at fortsætte, så længe hver forespørgsel, der øger din tæller, gør dette pålideligt. Dette kan fungere ganske godt.Dette foretager stadig en samlet forespørgsel for hvert opkald, hvilket (pr. næste mulighed) er unødvendigt, men det spammer i det mindste ikke fejlloggen som ovenstående muligheder.
-
Brug et bordbord. Det er, hvad jeg ville gøre. Enten i
bar
, eller i en sidetabel sombar_foo_counter
, anskaffe et række-id ved hjælp afUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counter
eller den mindre effektive mulighed, hvis dit framework ikke kan håndtere
RETURNING
:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;
Derefter i samme transaktion , brug den genererede tællerrække til
number
. Når du forpligter, tæller tabelrækken for denbar_id
bliver låst op til den næste forespørgsel, der skal bruges. Hvis du ruller tilbage, kasseres ændringen.
Jeg anbefaler tællertilgangen ved at bruge en dedikeret sidetabel til tælleren i stedet for at tilføje en kolonne til bar
. Det er renere at modellere, og det betyder, at du laver mindre opdateringsbloat i bar
, som kan sænke forespørgsler til bar
.