Ich habe schon einmal von solchen Parallelitätsproblemen in MySQL gehört. Nicht so bei Postgres.
Einbau-Zeilenebene Sperren in der READ COMMITTED
Standardtransaktionsisolationsstufe genug sind.
Ich schlage eine einzelne Anweisung mit einem datenmodifizierenden CTE vor (etwas, das MySQL auch nicht hat), da es praktisch ist, Werte direkt von einer Tabelle zur anderen zu übergeben (falls Sie dies benötigen sollten). Wenn Sie nichts aus der coupon
Tabelle benötigen, können Sie auch eine Transaktion mit separaten Anweisungen UPDATE
und INSERT
Anweisungen verwenden.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Es sollte selten vorkommen , dass mehr als eine Transaktion versucht, denselben Gutschein einzulösen. Sie haben eine eindeutige Nummer, nicht wahr? Mehr als eine Transaktion, die gleichzeitig versucht wird, sollte jedoch viel seltener sein. (Vielleicht ein Anwendungsfehler oder jemand, der versucht, das System zu spielen?)
Wie dem auch sei, die UPDATE
einzige ist für genau eine Transaktion erfolgreich, egal was passiert. A UPDATE
erwirbt vor dem Aktualisieren eine Sperre auf Zeilenebene für jede Zielzeile. Wenn eine gleichzeitige Transaktion versucht, UPDATE
dieselbe Zeile zu bearbeiten, wird die Sperre für die Zeile angezeigt und es wird gewartet, bis die blockierende Transaktion abgeschlossen ist ( ROLLBACK
oder COMMIT
). Dies ist die erste in der Sperrwarteschlange:
Wenn festgeschrieben, überprüfen Sie die Bedingung erneut. Wenn es immer noch ist NOT used
, sperren Sie die Zeile und fahren Sie fort. Andernfalls findet der UPDATE
jetzt keine qualifizierende Zeile und tut nichts , gibt keine Zeile zurück, also INSERT
tut der auch nichts.
Wenn Sie einen Rollback durchführen, sperren Sie die Zeile und fahren Sie fort.
Es gibt kein Potenzial für eine Rennbedingung .
Es besteht kein Potenzial für einen Deadlock, es sei denn, Sie schreiben mehr Schreibvorgänge in dieselbe Transaktion oder sperren auf andere Weise mehr Zeilen als nur eine.
Das INSERT
ist sorglos. Wenn aus Versehen das coupon_id
bereits in der log
Tabelle enthalten ist (und Sie eine EINZIGARTIGE oder PK-Einschränkung haben log.coupon_id
), wird die gesamte Transaktion nach einer eindeutigen Verletzung zurückgesetzt. Würde auf einen illegalen Status in Ihrer Datenbank hinweisen. Wenn die obige Anweisung die einzige Möglichkeit ist, in die log
Tabelle zu schreiben , sollte dies niemals vorkommen.