SQL Server Race-Bedingung Frage

7

(Hinweis: Dies ist für MS SQL Server)

Angenommen, Sie haben ein Tabellen-ABC mit einer Primärschlüssel-Identitätsspalte und einer CODE-Spalte. Wir möchten, dass jede Zeile einen eindeutigen, sequenziell generierten Code enthält (basierend auf einer typischen Prüfziffernformel).

Nehmen wir an, Sie haben eine andere Tabelle DEF mit nur einer Zeile, die den nächsten verfügbaren CODE speichert (stellen Sie sich eine einfache automatische Nummer vor).

Ich weiß, dass Logik wie unten eine Race-Bedingung darstellen würde, in der zwei Benutzer mit dem gleichen CODE enden könnten:

%Vor%

Ich weiß, dass zwei Benutzer in Schritt 1) ​​stecken bleiben könnten und am Ende mit dem gleichen CODE in der ABC-Tabelle enden könnten.

Was ist der beste Weg, um mit dieser Situation umzugehen? Ich dachte, ich könnte einfach ein "begin tran" / "commit tran" um diese Logik wickeln, aber ich denke nicht, dass es funktioniert hat. Ich hatte eine gespeicherte Prozedur wie diese zu testen, aber ich habe die Race-Bedingung nicht vermieden, als ich aus zwei verschiedenen Fenstern in MS lief:

%Vor%

Kann jemand etwas Licht darauf werfen? Ich dachte, die Transaktion würde verhindern, dass ein anderer Benutzer auf meine NextCodeTable zugreifen kann, bis die erste Transaktion abgeschlossen ist, aber ich denke, mein Verständnis von Transaktionen ist fehlerhaft.

EDIT: Ich habe versucht, die Wartezeit nach der "update" -Anweisung zu verschieben, und ich habe zwei verschiedene Codes ... aber ich vermutete das. Ich habe dort die waitfor-Anweisung, um eine Verzögerung zu simulieren, so dass der Race-Zustand leicht zu sehen ist. Ich denke, das Hauptproblem ist meine falsche Wahrnehmung der Funktionsweise von Transaktionen.

    
Hythloth 05.11.2009, 21:39
quelle

7 Antworten

6

Legen Sie die Transaktionsisolationsstufe auf Serializable fest.
Bei niedrigeren Isolationsstufen können andere Transaktionen die Daten in einer Zeile lesen, die in dieser Transaktion gelesen, aber noch nicht geändert wurde. Zwei Transaktionen können also den gleichen Wert lesen. Bei sehr geringer Isolation (Read Uncommitted) können andere Transaktionen Daten sogar lesen, nachdem sie geändert wurden (aber bevor sie festgeschrieben wurden) ...

Überprüfen Sie Details zu SQL Server-Isolationsstufen hier

Unter dem Strich ist also die Isolationsebene ein kritisches Stück, um zu steuern, auf welcher Ebene des Zugriffs andere Transaktionen in diese gelangen.

HINWEIS. Im Link finden Sie serialisierbare
Anweisungen kann keine Daten lesen, die von anderen Transaktionen geändert, aber noch nicht festgelegt wurden. .
Dies liegt daran, dass die Sperren beim Ändern der Zeile platziert werden, nicht wenn Begin Trans auftritt. Daher können Sie mit einer anderen Transaktion möglicherweise noch den alten Wert bis zu dem Punkt lesen, an dem Sie ihn geändert haben. Also würde ich die Logik ändern, um sie in derselben Anweisung zu ändern, wie Sie sie gelesen haben, und gleichzeitig die Sperre setzen.

%Vor%     
Charles Bretana 05.11.2009, 22:25
quelle
5

Wie andere Responder erwähnt haben, können Sie die Transaktionsisolationsstufe festlegen, um sicherzustellen, dass alles, was Sie mit einer SELECT-Anweisung "gelesen" haben, innerhalb einer Transaktion nicht geändert werden kann.

Alternativ können Sie eine Sperre speziell für die DEF-Tabelle entfernen, indem Sie nach dem Tabellennamen die Syntax WITH HOLDLOCK hinzufügen, z. B.

%Vor%

Es macht hier keinen großen Unterschied, da Ihre Transaktion klein ist, aber es kann nützlich sein, Sperren für einige SELECTs und nicht für andere innerhalb einer Transaktion zu entfernen. Es ist eine Frage der "Wiederholbarkeit gegen Nebenläufigkeit".

Ein paar relevante MS-SQL-Dokumente.

martin clayton 05.11.2009 22:46
quelle
4

Späte Antwort. Sie wollen eine Wettlaufsituation vermeiden ...

"SQL Server-Prozesswarteschlangen-Bedingung"

    
gbn 28.03.2011 18:50
quelle
3

Wiederholung:

  • Sie haben eine Transaktion begonnen. Dies "tut" nichts an und für sich selbst, es modifiziert das nachfolgende Verhalten
  • Sie lesen Daten aus einer Tabelle. Die Standardisolationsstufe lautet Read Committed, daher wird diese