Ich habe schon einmal von solchen Parallelitätsproblemen in MySQL gehört. Nicht so bei Postgres.
Einbau-Zeilenebene Sperren in der READ COMMITTEDStandardtransaktionsisolationsstufe 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 couponTabelle benötigen, können Sie auch eine Transaktion mit separaten Anweisungen UPDATEund INSERTAnweisungen 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 UPDATEeinzige ist für genau eine Transaktion erfolgreich, egal was passiert. A UPDATEerwirbt vor dem Aktualisieren eine Sperre auf Zeilenebene für jede Zielzeile. Wenn eine gleichzeitige Transaktion versucht, UPDATEdieselbe Zeile zu bearbeiten, wird die Sperre für die Zeile angezeigt und es wird gewartet, bis die blockierende Transaktion abgeschlossen ist ( ROLLBACKoder 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 UPDATEjetzt keine qualifizierende Zeile und tut nichts , gibt keine Zeile zurück, also INSERTtut 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 INSERTist sorglos. Wenn aus Versehen das coupon_idbereits in der logTabelle 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 logTabelle zu schreiben , sollte dies niemals vorkommen.