Angenommen, Sie haben den folgenden Code (bitte ignorieren Sie, dass er schrecklich ist):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
Meines Erachtens verwaltet dies die Parallelität NICHT ordnungsgemäß. Nur weil Sie eine Transaktion haben, heißt das nicht, dass jemand anderes nicht den gleichen Wert wie vor der Update-Anweisung liest.
Lassen Sie nun den Code unverändert (ich weiß, dass dies besser als einzelne Anweisung oder noch besser als automatische Inkrementierung / Identitätsspalte gehandhabt wird). Auf welche Weise können Sie sicherstellen, dass der gemeinsame Zugriff ordnungsgemäß verarbeitet wird, und verhindern, dass zwei Clients dieselben Bedingungen erfüllen ID-Wert?
Ich bin mir ziemlich sicher, dass das Hinzufügen von a WITH (UPDLOCK, HOLDLOCK)
zum SELECT den Trick macht. Die SERIALIZABLE-Transaktionsisolationsstufe scheint ebenfalls zu funktionieren, da sie es jedem anderen verweigert, zu lesen, was Sie getan haben, bis die Übertragung beendet ist ( UPDATE : Dies ist falsch. Siehe Martins Antwort). Ist das wahr? Funktionieren beide gleich gut? Wird einer dem anderen vorgezogen?
Stellen Sie sich vor, Sie tun etwas Berechtigteres als ein ID-Update - einige Berechnungen basieren auf einem Lesevorgang, den Sie aktualisieren müssen. Es kann viele Tabellen geben, an die Sie schreiben und an die Sie nicht schreiben. Was ist hier die beste Praxis?
Nachdem Sie diese Frage geschrieben haben, sind die Sperrhinweise meines Erachtens besser, da Sie dann nur die Tabellen sperren, die Sie benötigen, aber ich würde mich über jede Eingabe freuen.
PS Und nein, ich kenne die beste Antwort nicht und möchte wirklich ein besseres Verständnis bekommen! :)
update
, der möglicherweise auf veralteten Daten basiert? In letzteremrowversion
Fall können Sie mithilfe der Spalte überprüfen, ob die zu aktualisierende Zeile seit dem Lesen nicht mehr geändert wurde.Antworten:
Ich spreche nur den
SERIALIZABLE
Aspekt der Isolationsstufe an. Ja, das wird funktionieren, aber mit Deadlock-Risiko.Zwei Transaktionen können die Zeile gleichzeitig lesen. Sie werden sich nicht gegenseitig blockieren, da sie abhängig von der Tabellenstruktur entweder eine Objektsperre
S
oder Indexsperren annehmenRangeS-S
und diese Sperren kompatibel sind . Sie blockieren sich jedoch gegenseitig, wenn sie versuchen, die für die Aktualisierung erforderlichen Sperren (ObjektsperreIX
bzw. IndexRangeS-U
) abzurufen, die zu einem Deadlock führen.Die Verwendung eines expliziten
UPDLOCK
Hinweises serialisiert stattdessen die Lesevorgänge und vermeidet so das Deadlock-Risiko.quelle
IX
,X
auf dem Haufen selbst von nach umzurechnen . Interessanterweise qualifizieren sich keine Zeilen, so dass niemals Zeilensperren aufgehoben werden. Nicht sicher, warum es überhaupt dasX
Schloss nimmt .Ich denke, der beste Ansatz für Sie wäre, Ihr Modul tatsächlich einer hohen Parallelität auszusetzen und sich selbst davon zu überzeugen. Manchmal reicht UPDLOCK alleine und HOLDLOCK ist nicht erforderlich. Manchmal funktioniert sp_getapplock sehr gut. Ich würde hier keine pauschale Aussage treffen - manchmal ändert das Hinzufügen eines weiteren Index, Triggers oder einer indizierten Ansicht das Ergebnis. Wir müssen den Testcode stressen und uns von Fall zu Fall überzeugen.
Ich habe mehrere Beispiele von Stresstests geschrieben hier
Bearbeiten: Für eine bessere Kenntnis der Interna können Sie die Bücher von Kalen Delaney lesen. Es kann jedoch vorkommen, dass Bücher nicht mehr synchron sind, wie bei jeder anderen Dokumentation. Außerdem sind zu viele Kombinationen zu berücksichtigen: sechs Isolationsstufen, viele Arten von Sperren, gruppierte / nicht gruppierte Indizes und wer weiß was noch. Das sind viele Kombinationen. Darüber hinaus ist SQL Server Closed Source, sodass wir keinen Quellcode herunterladen, debuggen und so weiter können - das wäre die ultimative Wissensquelle. Alles andere ist möglicherweise nach der nächsten Version oder dem nächsten Service Pack unvollständig oder veraltet.
Sie sollten sich also nicht entscheiden, was für Ihr System funktioniert, ohne Ihre eigenen Stresstests durchzuführen. Was auch immer Sie gelesen haben, es kann Ihnen helfen zu verstehen, was vor sich geht, aber Sie müssen beweisen, dass der Rat, den Sie gelesen haben, für Sie funktioniert. Ich glaube, niemand kann das für Sie tun.
quelle
In diesem speziellen Fall würde die Hinzufügung einer
UPDLOCK
SperreSELECT
tatsächlich Anomalien verhindern. Das Hinzufügen vonHOLDLOCK
ist nicht erforderlich, da eine Aktualisierungssperre für die Dauer der Transaktion aufrechterhalten wird, aber ich gebe zu, dies in der Vergangenheit selbst als (möglicherweise schlechte) Angewohnheit aufgenommen zu haben.Es gibt keine Best Practice. Die Wahl der Parallelitätskontrolle muss sich nach den Anforderungen der Anwendung richten. Einige Anwendungen / Transaktionen müssen so ausgeführt werden, als ob sie das ausschließliche Eigentum an der Datenbank hätten, wodurch Anomalien und Ungenauigkeiten um jeden Preis vermieden werden. Andere Anwendungen / Transaktionen können ein gewisses Maß an gegenseitigen Störungen tolerieren.
Edit: @ AlexKuznetsovs Kommentar veranlasste mich, die Frage erneut zu lesen und den sehr offensichtlichen Fehler in meiner Antwort zu beseitigen. Hinweis für Selbstversorger bei verspäteter Veröffentlichung.
quelle