Während Erwins Vorschlag möglicherweise der einfachste ist Weg, um das richtige Verhalten zu erhalten (solange Sie Ihre Transaktion wiederholen, wenn Sie eine Ausnahme mit SQLSTATE
erhalten von 40001) arbeiten Warteschlangenanwendungen von Natur aus besser mit Anfragen, die für eine Chance blockiert werden, an der Warteschlange an der Reihe zu sein, als mit der PostgreSQL-Implementierung von SERIALIZABLE
Transaktionen, was eine höhere Gleichzeitigkeit ermöglicht und etwas "optimistischer" in Bezug auf die Wahrscheinlichkeit von Kollisionen ist.
Die Beispielabfrage in der Frage, so wie sie ist, im Standard READ COMMITTED
Die Transaktionsisolationsstufe würde es zwei (oder mehr) gleichzeitigen Verbindungen ermöglichen, beide dieselbe Zeile aus der Warteschlange zu "beanspruchen". Folgendes wird passieren:
- T1 startet und kommt so weit, die Zeile im
UPDATE
zu sperren Phase. - T2 überlappt T1 in der Ausführungszeit und versucht, diese Zeile zu aktualisieren. Es blockiert bis zum
COMMIT
oderROLLBACK
von T1. - T1 wird übergeben, nachdem die Zeile erfolgreich "beansprucht" wurde.
- T2 versucht, die Zeile zu aktualisieren, stellt fest, dass T1 dies bereits getan hat, sucht nach der neuen Version der Zeile, stellt fest, dass sie immer noch die Auswahlkriterien erfüllt (das ist nur diese
id
Übereinstimmungen) und "beansprucht" auch die Zeile.
Es kann so modifiziert werden, dass es korrekt funktioniert (wenn Sie eine Version von PostgreSQL verwenden, die FOR UPDATE
zulässt Klausel in einer Unterabfrage). Fügen Sie einfach FOR UPDATE
hinzu bis zum Ende der Unterabfrage, die die ID auswählt, und dies geschieht:
- T1 startet und sperrt nun die Zeile vor dem Auswählen die ID.
- T2 überschneidet sich mit T1 in der Ausführungszeit und blockiert, während versucht wird, eine ID auszuwählen, während das
COMMIT
ansteht oderROLLBACK
von T1. - T1 wird übergeben, nachdem die Zeile erfolgreich "beansprucht" wurde.
- Bis T2 lesen kann die Zeile, um die ID zu sehen, sieht es, dass es beansprucht wurde, also findet es die nächste verfügbare ID.
Beim REPEATABLE READ
oder SERIALIZABLE
Transaktionsisolationsstufe, würde der Schreibkonflikt einen Fehler auslösen, den Sie abfangen und basierend auf dem SQLSTATE feststellen könnten, dass es sich um einen Serialisierungsfehler handelt, und es erneut versuchen.
Wenn Sie im Allgemeinen SERIALIZABLE-Transaktionen wünschen, aber Wiederholungen im Warteschlangenbereich vermeiden möchten, können Sie dies möglicherweise durch die Verwendung einer Advisory-Sperre erreichen.