Deadlock für SQL Server-Indexaktualisierung


13

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

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

update table1 set column1 = value1 where id = @Id

Nimmt X-Lock für Tabelle1 und versucht dann, Index1 mit X-Lock zu versehen.

Abfrage 2:

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

Nimmt eine S-Sperre für index1 vor und versucht dann, eine S-Sperre für table1 durchzuführen.

Gibt es eine Möglichkeit, den Deadlock zu verhindern, während dieselben Abfragen beibehalten werden? Kann ich zum Beispiel in der Update-Transaktion vor dem Update eine X-Sperre für den Index vornehmen, um sicherzustellen, dass der Zugriff auf die Tabelle und den Index in derselben Reihenfolge erfolgt - was sollte den Deadlock verhindern?

Isolationsstufe ist Read Committed. Zeilen- und Seitensperren sind für die Indizes aktiviert. Es ist möglich, dass derselbe Datensatz an beiden Abfragen teilnimmt. Ich kann dies nicht anhand des Deadlock-Diagramms erkennen, da die Parameter nicht angezeigt werden.

Deadlock-Diagramm

Antworten:


11

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) verknüpft ist:

Deadlock-Diagramm

Wie die Frage feststellt, besteht das allgemeine Deadlock-Risiko, da die Abfragen möglicherweise inkompatible Sperren für dieselben Ressourcen in unterschiedlichen Reihenfolgen erhalten. Die SELECTAbfrage muss aufgrund der RID-Suche auf den Index vor der Tabelle zugreifen, während die UPDATEAbfrage 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 den nicht gruppierten Index abdecken. Dies ist in Ihrem Fall wahrscheinlich nicht praktikabel, da die SELECTAbfrage 26 Spalten zurückgibt.
  2. Vermeiden Sie die RID-Suche, indem Sie einen Clustered-Index erstellen. Dazu müsste ein Clustered-Index für die Spalte erstellt werden Proposal. Dies ist eine Überlegung wert, obwohl es den Anschein hat, dass diese Spalte vom Typ ist uniqueidentifier, was in Abhängigkeit von weiter gefassten Themen eine gute Wahl für einen Clustered-Index sein kann oder nicht.
  3. Vermeiden Sie es, beim Lesen gemeinsame Sperren zu verwenden, indem Sie die Datenbankoptionen READ_COMMITTED_SNAPSHOToder aktivieren SNAPSHOT. Dies würde sorgfältige Tests erfordern, insbesondere in Bezug auf das Verhalten der eingebauten Blockierung. Der Auslösecode muss auch getestet werden, um sicherzustellen, dass die Logik korrekt funktioniert.
  4. Vermeiden Sie es, beim Lesen gemeinsame Sperren zu verwenden, indem Sie die READ UNCOMMITTEDIsolationsstufe für die SELECTAbfrage verwenden. Es gelten alle üblichen Vorsichtsmaßnahmen.
  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 sich dies möglicherweise auf andere Anfragen auswirkt, nicht nur auf die beiden in der Frage identifizierten.

Kann ich in der Update-Transaktion vor dem Update irgendwie eine X-Sperre für den Index durchführen, um sicherzustellen, dass der Zugriff auf die Tabelle und den Index in derselben Reihenfolge erfolgt?

Sie können dies versuchen, indem Sie das Update in eine explizite Transaktion einschließen und vor dem Update SELECTeinen XLOCKHinweis auf den nicht gruppierten Indexwert ausführen. Dies setzt voraus, dass Sie mit Sicherheit wissen, wie hoch der aktuelle Wert im nicht gruppierten Index ist, den Ausführungsplan korrekt ausführen und alle Nebenwirkungen dieser zusätzlichen Sperre korrekt antizipieren. Es hängt auch davon ab, dass die Locking-Engine nicht intelligent genug ist, um zu verhindern, dass das Schloss geschlossen wird, wenn es als redundant eingestuft wird .

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


Wenn ich mich näher mit dem Thema befasse, ist es wahrscheinlich am besten, es unverändert zu lassen. Es ist ein häufigeres Problem, das ich ursprünglich erkannt habe.
Dale K

1

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

  1. In set deadlock priority low;die Auswahl. Dadurch wird diese Abfrage zum Deadlock-Opfer, wenn ein Deadlock auftritt.
  2. Richten Sie die Wiederholungslogik in Ihrer Anwendung ein, um die Auswahl automatisch zu wiederholen, wenn sie aufgrund eines Deadlocks (oder einer Zeitüberschreitung) fehlschlägt, nachdem Sie eine kurze Zeit lang gewartet / geschlafen haben, damit die blockierenden Abfragen abgeschlossen werden können.

Hinweis: Wenn Sie selectTeil einer expliziten Transaktion mit mehreren Anweisungen sind, müssen Sie sicherstellen, dass Sie die gesamte Transaktion wiederholen und nicht nur die fehlgeschlagene Anweisung. Andernfalls können unerwartete Ergebnisse auftreten. Wenn dies ein einziges ist , selectdann sind Sie in Ordnung, aber wenn es Aussage ist xvon ninnerhalb einer Transaktion, dann nur sicherstellen , wiederholen Sie alle nAnweisungen während der Wiederholung.


Thanks-Abfragen sind standardmäßig automatisch Deadlock-Opfer. Und ja, wir haben bereits einen soliden Wiederholungsmechanismus eingerichtet.
Dale K
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.