it-swarm.com.de

Deadlock für die Aktualisierung des SQL Server-Index

Ich habe 2 Abfragen, die bei gleichzeitiger Ausführung einen Deadlock verursachen.

Abfrage 1 - Aktualisieren Sie eine Spalte, die in einem Index enthalten ist (Index1):

update table1 set column1 = value1 where id = @Id

Nimmt X-Lock für Tabelle1 und versucht dann eine X-Sperre für Index1.

Abfrage 2:

select columnx, columny, etc from table1 where {some condition}

Nimmt eine S-Sperre für Index1 und versucht dann eine S-Sperre für Tabelle1.

Gibt es eine Möglichkeit, den Deadlock zu verhindern, während dieselben Abfragen beibehalten werden? Kann ich beispielsweise vor der Aktualisierung in der Aktualisierungstransaktion eine X-Sperre für den Index vornehmen, um sicherzustellen, dass der Tabellen- und Indexzugriff in derselben Reihenfolge erfolgt - was sollte den Deadlock verhindern?

Die Isolationsstufe ist Read Committed. Zeilen- und Seitensperren sind für die Indizes aktiviert. Es ist möglich, dass derselbe Datensatz an beiden Abfragen beteiligt ist - ich kann es dem Deadlock-Diagramm nicht entnehmen, da es die Parameter nicht anzeigt.

Deadlock-Diagramm

13
Dale K

Gibt es eine Möglichkeit, den Deadlock zu verhindern, während dieselben Abfragen beibehalten werden?

Das Deadlock-Diagramm zeigt, dass es sich bei diesem bestimmten Deadlock um einen Conversion-Deadlock handelt, der mit einer Lesezeichensuche (in diesem Fall einer RID-Suche) verbunden ist:

Deadlock graph

Wie die Frage feststellt, besteht das allgemeine Deadlock-Risiko, weil die Abfragen möglicherweise inkompatible Sperren für dieselben Ressourcen in unterschiedlichen Reihenfolgen erhalten. Die Abfrage SELECT muss aufgrund der RID-Suche auf den Index vor der Tabelle zugreifen, während die Abfrage UPDATE zuerst die Tabelle und dann den Index ändert.

Um den Deadlock zu beseitigen, muss einer der Deadlock-Bestandteile entfernt werden. Das Folgende sind die Hauptoptionen:

  1. Vermeiden Sie die RID-Suche, indem Sie die nicht gruppierte Indexabdeckung vornehmen. Dies ist in Ihrem Fall wahrscheinlich nicht praktikabel, da die Abfrage SELECT 26 Spalten zurückgibt.
  2. Vermeiden Sie die RID-Suche, indem Sie einen Clustered-Index erstellen. Dies würde das Erstellen eines Clustered-Index für die Spalte Proposal beinhalten. Dies ist eine Überlegung wert, obwohl es den Anschein hat, dass diese Spalte vom Typ uniqueidentifier ist, was je nach allgemeinen Aspekten eine gute Wahl für einen Clustered-Index sein kann oder nicht.
  3. Vermeiden Sie beim Lesen gemeinsame Sperren, indem Sie die Datenbankoptionen READ_COMMITTED_SNAPSHOT Oder SNAPSHOT aktivieren. Dies würde sorgfältige Tests erfordern, insbesondere im Hinblick auf ein bestimmtes Blockierungsverhalten. Der Triggercode würde auch Tests erfordern, um sicherzustellen, dass die Logik korrekt funktioniert.
  4. Vermeiden Sie beim Lesen gemeinsame Sperren, indem Sie die Isolationsstufe READ UNCOMMITTED Für die Abfrage SELECT verwenden. Es gelten alle üblichen Einschränkungen.
  5. Vermeiden Sie die gleichzeitige Ausführung der beiden fraglichen Abfragen, indem Sie eine exklusive Anwendungssperre verwenden (siehe sp_getapplock ).
  6. Verwenden Sie Hinweise zur Tabellensperre, um Parallelität zu vermeiden. Dies ist ein größerer Hammer als Option 5, da er andere Abfragen betreffen kann, nicht nur die beiden in der Frage angegebenen.

Kann ich den Index in der Aktualisierungstransaktion vor dem Update irgendwie mit einem X-Lock versehen, um sicherzustellen, dass der Tabellen- und Indexzugriff in derselben Reihenfolge sind ?

Sie können dies versuchen, indem Sie das Update in eine explizite Transaktion einschließen und vor dem Update einen SELECT mit einem XLOCK Hinweis auf den nicht gruppierten Indexwert ausführen. Dies setzt voraus, dass Sie sicher wissen, wie hoch der aktuelle Wert im nicht gruppierten Index ist, dass der Ausführungsplan richtig ist und dass Sie alle Nebenwirkungen dieser zusätzlichen Sperre richtig antizipieren. Es hängt auch davon ab, dass die Verriegelungsmaschine nicht intelligent genug ist, um das Aufheben der Verriegelung zu vermeiden, wenn sie als redundant beurteilt ist.

Kurz gesagt, obwohl dies im Prinzip machbar ist, empfehle ich es nicht. Es ist zu leicht, etwas zu verpassen oder sich auf kreative Weise auszutricksen. Wenn Sie diese Deadlocks wirklich vermeiden müssen (anstatt sie nur zu erkennen und erneut zu versuchen), würde ich Sie ermutigen, stattdessen die oben aufgeführten allgemeineren Lösungen zu betrachten.

11
Paul White 9

Ich habe ein ähnliches Problem, das gelegentlich auftritt, und hier ist der Ansatz, den ich verfolge.

  1. Hinzufügen set deadlock priority low; zur Auswahl. Dies führt dazu, dass diese Abfrage das Deadlock-Opfer ist, wenn ein Deadlock auftritt.
  2. Richten Sie in Ihrer Anwendung eine Wiederholungslogik ein, um die Auswahl automatisch zu wiederholen, wenn sie aufgrund eines Deadlocks (oder einer Zeitüberschreitung) fehlschlägt, nachdem Sie eine kurze Zeit gewartet/geschlafen haben, damit die blockierenden Abfragen abgeschlossen werden können.

Hinweis: Wenn Ihr select Teil einer expliziten Transaktion mit mehreren Anweisungen ist, müssen Sie sicherstellen, dass Sie die gesamte Transaktion wiederholen und nicht nur die Anweisung, die fehlgeschlagen ist. Andernfalls können unerwartete Ergebnisse erzielt werden. Wenn dies ein einzelnes select ist, ist alles in Ordnung. Wenn es sich jedoch um die Anweisung x von n innerhalb einer Transaktion handelt, stellen Sie einfach sicher, dass Sie alle n Anweisungen während des erneuten Versuchs.

1
BateTech