Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Ist eine einzelne SQL Server-Anweisung atomar und konsistent?

Ich bin davon ausgegangen, dass eine einzelne Anweisung in SQL Server konsistent ist

Diese Annahme ist falsch. Die folgenden beiden Transaktionen haben eine identische Sperrsemantik:

STATEMENT

BEGIN TRAN; STATEMENT; COMMIT

Überhaupt kein Unterschied. Einzelne Anweisungen und Auto-Commits ändern nichts.

Es hilft also nicht, die gesamte Logik in einer Aussage zu verschmelzen (falls doch, war es ein Zufall, weil sich der Plan geändert hat).

Lassen Sie uns das vorliegende Problem beheben. SERIALIZABLE behebt die Inkonsistenz, die Sie sehen, da es garantiert, dass sich Ihre Transaktionen so verhalten, als ob sie Single-Threaded ausgeführt würden. Entsprechend verhalten sie sich so, als ob sie sofort ausgeführt würden.

Sie werden Deadlocks bekommen. Wenn Sie mit einer Wiederholungsschleife einverstanden sind, sind Sie an dieser Stelle fertig.

Wenn Sie mehr Zeit investieren möchten, wenden Sie Sperrhinweise an, um den exklusiven Zugriff auf die relevanten Daten zu erzwingen:

UPDATE Gifts  -- U-locked anyway
SET GivenAway = 1
WHERE GiftID = (
   SELECT TOP 1 GiftID
   FROM Gifts WITH (UPDLOCK, HOLDLOCK) --this normally just S-locks.
   WHERE g2.GivenAway = 0
    AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
   ORDER BY g2.GiftValue DESC
)

Sie sehen jetzt eine reduzierte Parallelität. Das kann je nach Auslastung völlig in Ordnung sein.

Die Art Ihres Problems erschwert das Erreichen von Parallelität. Wenn Sie dafür eine Lösung benötigen, müssen wir invasivere Techniken anwenden.

Sie können das UPDATE etwas vereinfachen:

WITH g AS (
   SELECT TOP 1 Gifts.*
   FROM Gifts
   WHERE g2.GivenAway = 0
    AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
   ORDER BY g2.GiftValue DESC
)
UPDATE g  -- U-locked anyway
SET GivenAway = 1

Dadurch wird ein unnötiger Join entfernt.