Ihre Optionen sind:
-
Führen Sie
SERIALIZABLEaus Isolation. Interdependente Transaktionen werden beim Festschreiben abgebrochen, da sie einen Serialisierungsfehler aufweisen. Sie werden viel Fehlerprotokoll-Spam erhalten und viele Wiederholungen unternehmen, aber es wird zuverlässig funktionieren. -
Definieren Sie einen
UNIQUEeinschränken und bei einem Fehler erneut versuchen, wie Sie bemerkt haben. Gleiche Probleme wie oben. -
Wenn es ein übergeordnetes Objekt gibt, können Sie
SELECT ... FOR UPDATEdas übergeordnete Objekt, bevor Sie Ihrmaxausführen Anfrage. In diesem Fall würden SieSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE. Sie verwendenbarals Sperre für allefoos mit dieserbar_id. Sie können dann sicher sein, dass Sie fortfahren können, solange jede Abfrage, die Ihren Zähler erhöht, dies zuverlässig tut. Das kann ganz gut funktionieren.Dies führt immer noch eine aggregierte Abfrage für jeden Aufruf durch, was (für die nächste Option) unnötig ist, aber zumindest nicht das Fehlerprotokoll wie die obigen Optionen spammt.
-
Verwenden Sie einen Tresentisch. Das würde ich tun. Entweder in
bar, oder in einer Seitentabelle wiebar_foo_counter, erhalten Sie eine Zeilen-ID mitUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counteroder die weniger effiziente Option, wenn Ihr Framework
RETURNINGnicht verarbeiten kann :SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;Dann in derselben Transaktion verwenden Sie die generierte Zählerzeile für die
number. Beim Festschreiben die Zählertabellenzeile für diesebar_idwird für die nächste zu verwendende Abfrage entsperrt. Bei einem Rollback wird die Änderung verworfen.
Ich empfehle den Counter-Ansatz, indem ich einen dedizierten Seitentisch für den Counter verwende, anstatt eine Spalte zu bar hinzuzufügen . Das ist sauberer zu modellieren und bedeutet, dass Sie weniger Update-Bloat in bar erzeugen , was Abfragen an bar verlangsamen kann .