PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Atomic UPDATE .. SELECT in Postgres

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 oder ROLLBACK 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 oder ROLLBACK 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.