Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 10 Min.

Die sechs Gesichter der Deadlocks

Wie Sie verhindern, dass sich zwei Transaktionen gegenseitig blockieren.
Schon in den beiden vorangegangenen Teilen dieser Mini­serie [1] [2] haben Sie erfahren, dass der SQL Server gleichzeitig arbeitende Benutzer abhängig vom gewählten Transaction Isolation Level voneinander isoliert. Dafür verwendet er diese Locks:
  • Für schreibende Transaktionen werden immer exklusive Locks angefordert.
  • Lesende Transaktionen können abhängig vom gewählten Transaction Isolation Level Shared Locks anfordern.
So weit, so gut. Das große Problem von Locks ist aber, dass diese nicht alle kompatibel zueinander sind. Ein Shared Lock ist zum Beispiel nicht kompatibel mit einem Exclusive Lock. Fordern Sie trotzdem einen Shared Lock an, wird dieser erst geliefert, wenn der exklusive Lock nicht mehr aktiv ist, es entsteht eine klassische Blocking-Situation.Kritisch wird es, wenn die zweite Session ebenfalls einen inkompatiblen Lock anfordert. Dann wartet die erste Session auf die zweite, während die zweite auf die erste Session wartet. Nichts geht mehr, es ist eine Deadlocking-Situation entstanden.

Deadlock-Handling im SQL Server

Das Nette am SQL Server ist, dass er Deadlock-Situationen selbstständig auflösen kann. Das erledigt ein Hintergrundprozess namens Deadlock Monitor, der alle paar Sekunden prüft, ob Deadlocks vorhanden sind.Hat er eine Deadlocking-Situation identifiziert, macht er die günstigste Transaktion rückgängig. Dadurch werden die angeforderten Locks wieder freigegeben und die blockierte Transaktion kann mit ihrer Arbeit fortfahren. Die günstigste Transaktion ist dabei diejenige, welche die wenigsten Daten ins Transaktions-Log geschrieben hat. Bei einem Deadlock zwischen einer lesenden und einer schreibenden Trans­aktion wird deshalb immer die lesende Transaktion zurückgerollt, da diese keine Daten ins Transaktions-Log schreibt. Die vom Deadlock Monitor zurückgerollte Transaktion wird auch als Deadlock Victim bezeichnet.Die als Deadlock Victim ausgewählte Transaktion erhält die Fehlernummer 1205. Tritt diese Meldung auf, sollte der Datenzugriffscode der Anwendung die Transaktion einfach noch einmal ausführen. Da der Deadlock seitens des SQL Server beim Ausgeben der Fehlernummer bereits aufgelöst wurde, sollte beim nächsten Versuch kein Deadlock mehr auftreten. Allerdings sollte der Code nicht unendlich lange versuchen, eine mit dem Fehler 1205 quittierte Transaktion erneut auszuführen, sondern die Versuche nach mehrmaligem Fehlschlagen beenden und den Benutzer informieren, dass es Probleme beim Zugriff auf die Datenbank gibt.Ein Deadlock ist aber eine Situation, die Ihre Anwendung in den meisten Fällen ohne Benutzerintervention lösen kann – solange Ihr Datenzugriffscode stets das Auftreten der Fehlernummer 1205 überprüft und angemessen darauf reagiert.

Deadlock-Troubleshooting

Das Bordwerkzeug des SQL Server enthält drei Technologien­ zur Analyse und Behandlung von Deadlocking-Situationen:
  • Trace Flag 1222
  • SQL Server Profiler
  • Extended Events
Generell empfehle ich den Einsatz von Extended Events für den Umgang mit Deadlocks. Da es sich beim SQL Server Profiler bereits um eine von Microsoft abgekündigte Funktion (deprecated) handelt, wird sie in einer der nächsten Versionen der Datenbank nicht mehr enthalten sein. Das Trace Flag 1222 passt nicht mehr so recht in die Zeit, da hier lediglich XML-Informationen über den aufgetretenen Deadlock ins SQL-Server-Log geschrieben werden (Bild 1).
Die leistungsfähigste Möglichkeit zum Troubleshooting bei Deadlocks bieten die Extended Events, da der SQL Server hier eine Vielzahl unterschiedlicher Informationen in Form eines XML-Dokuments zurückliefert. Bild 2 zeigt einen Ausschnitt daraus. Es handelt sich dabei um den sogenannten Deadlock-Graphen, der die folgenden XML-Knoten auf oberster Ebene beinhaltet:
  • <process-list>
  • <resource-list>
Der Knoten <process-list> beschreibt, welche Abfragen beim Deadlock beteiligt waren. Dadurch können Sie sehr leicht identifizieren, bei welchen Transaktionen der Deadlock aufgetreten ist. Zudem liefert der Knoten Informationen darüber, welche Ressourcen (Locks) beim abgebildeten Deadlock involviert waren.Sie sehen über eine <owner-List>, welche Locks bereits von den Transaktionen gehalten wurden, und die <waiter-List> gibt Auskunft, auf welche Locks aktuell gewartet wurde. Zusätzlich sind <owner-List> und <waiter-List> mit der entsprechenden Transaktion in der <process-list> verknüpft.Anhand dieser Informationen im Deadlock-Graphen lässt sich recht einfach herausfinden, warum der Deadlock aufgetreten ist. Generell ist der Deadlock-Graph die erste Anlaufstelle, wenn Sie eine Deadlocking-Situation analysieren.Ich bekomme sehr oft E-Mail-Anfragen, warum zwei verschiedene Abfragen in einen Deadlock gelaufen sind. Meist sind im Anhang der Mails noch die zugehörigen SQL-Statements zu finden. Das bringt aber nicht viel, da der Deadlock im SQL Server auf physischer Ebene auftritt, die SQL-Statements aber immer auf logischer Ebene beschreiben, was die Datenbank tun soll. Daher fordere ich für die Analyse immer den zugehörigen Deadlock-Graphen an, da nur dieser beschreibt, was auf physischer Ebene passiert ist. Ohne Deadlock-Graph ist die Analyse fast unmöglich!

Cycle Deadlock

Wie oben schon angedeutet, gibt es verschiedene Arten von Deadlocks. Die einfachste davon ist ein Cycle Deadlock. Er ensteht, wenn auf Ressourcen in unterschiedlicher Reihenfolge zugegriffen wird. Sehen Sie sich dazu Listing 1 und 2 an. Wie zu erkennen ist, greifen die beiden Sessions auf die­selben beiden Tabellen zu, aber in unterschiedlicher Reihenfolge: Die erste Session greift auf Tabelle T1 und dann auf ­Tabelle T2 zu, die zweite Session greift auf Tabelle T2 und danach auf Tabelle T1 zu.
Listing 1: Cycle Deadlock – Session 1
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; TempDB &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TABLE&lt;/span&gt; T1 &lt;br/&gt;( &lt;br/&gt;   C1 &lt;span class="hljs-built_in"&gt;INT&lt;/span&gt; &lt;br/&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;INSERT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; T1 &lt;span class="hljs-keyword"&gt;VALUES&lt;/span&gt; (&lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TABLE&lt;/span&gt; T2 &lt;br/&gt;( &lt;br/&gt;   C1 &lt;span class="hljs-built_in"&gt;INT&lt;/span&gt; &lt;br/&gt;) &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;INSERT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; T2 &lt;span class="hljs-keyword"&gt;VALUES&lt;/span&gt; (&lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; T1 &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; T2 &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;UPDATE&lt;/span&gt; T1 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; C1 = &lt;span class="hljs-number"&gt;2&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;UPDATE&lt;/span&gt; T2 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; C1 = &lt;span class="hljs-number"&gt;2&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-comment"&gt;-- COMMIT TRANSACTION &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;DROP TABLE T1 &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;DROP TABLE T2 &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;GO &lt;/span&gt; 
Wenn Sie die Statements Schritt für Schritt abwechselnd für die beiden Transaktionen durchführen, werden Sie in einen Deadlock laufen, der ein paar Sekunden später durch den Deadlock Monitor erkannt und schlussendlich durch das Zurückrollen der günstigsten Transaktion aufgelöst wird.
Listing 2: Cycle Deadlock – Session 2
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; TempDB &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;UPDATE&lt;/span&gt; T2 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; C1 = &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;UPDATE&lt;/span&gt; T1 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; C1 = &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;COMMIT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt;  
Dieser zyklische Deadlock lässt sich ganz einfach vermeiden: Es genügt, wenn Sie auf Tabellen immer in der gleichen Reihenfolge zugreifen. Ich habe schon mehrfach mit Kunden zusammengearbeitet, deren Entwickler angehalten waren, auf Tabellen immer in alphabetischer Reihenfolge (auf Basis der Namensgebung) zuzugreifen, um Cycle Deadlocks zu vermeiden.

Bookmark Lookup Deadlocks

Ich habe schon einige Male in der dotnetpro über Bookmark Lookups geschrieben – beispielsweise in [3] – und berichtet, dass diese hinsichtlich Performance und Indizierungsstrategie nicht immer das beste Mittel sind. Ein weiterer negativer Seiteneffekt, der sich aus Bookmark Lookups ergeben kann, ist die Tatsache, dass Sie hier ganz leicht in Deadlocks laufen, wenn Sie parallele schreibende Transaktionen auf derselben Tabelle ausführen. Sehen Sie sich dazu Listing 3 und 4 an.
Listing 3: Bookmark Lookup Deadlock – Session 1
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; &lt;span class="hljs-keyword"&gt;master&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; &lt;span class="hljs-keyword"&gt;DATABASE&lt;/span&gt; BookmarkLookupDL &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; BookmarkLookupDL &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TABLE&lt;/span&gt; Deadlock ( &lt;br/&gt;   Col1 &lt;span class="hljs-built_in"&gt;INT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;NOT&lt;/span&gt; &lt;span class="hljs-literal"&gt;NULL&lt;/span&gt; PRIMARY &lt;span class="hljs-keyword"&gt;KEY&lt;/span&gt; CLUSTERED, &lt;br/&gt;   Col2 &lt;span class="hljs-built_in"&gt;INT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;NOT&lt;/span&gt; &lt;span class="hljs-literal"&gt;NULL&lt;/span&gt;, &lt;br/&gt;   Col3 &lt;span class="hljs-built_in"&gt;INT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;NOT&lt;/span&gt; &lt;span class="hljs-literal"&gt;NULL&lt;/span&gt; &lt;br/&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; NONCLUSTERED &lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt; idx_Col3 &lt;span class="hljs-keyword"&gt;ON&lt;/span&gt; Deadlock(Col3) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;INSERT&lt;/span&gt; &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Deadlock &lt;span class="hljs-keyword"&gt;VALUES&lt;/span&gt; (&lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Deadlock &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;ISOLATION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;LEVEL&lt;/span&gt; REPEATABLE &lt;span class="hljs-keyword"&gt;READ&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;WHILE&lt;/span&gt; (&lt;span class="hljs-number"&gt;1&lt;/span&gt; = &lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;UPDATE&lt;/span&gt; Deadlock &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; Col1 = Col1 + &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; Col3 = &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;END&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt;  
Die Listings sind nicht komplex. In Listing 3 wird in einer Endlosschleife der Clustered Key der Tabelle aktualisiert. Da der Clustered Key auch als logischer Zeiger im Non-Clustered­ Index vorhanden ist, muss der SQL Server zunächst den Clustered Index und danach auch den Non-Clustered Index aktualisieren. Für das Aktualisieren beider Indizes sind jeweils exklusive Locks erforderlich.
Listing 4: Bookmark Lookup Deadlock – Session 2
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; BookmarkLookupDL &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;ISOLATION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;LEVEL&lt;/span&gt; REPEATABLE &lt;span class="hljs-keyword"&gt;READ&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;WHILE&lt;/span&gt; (&lt;span class="hljs-number"&gt;1&lt;/span&gt; = &lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Deadlock &lt;span class="hljs-keyword"&gt;WITH&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt;(idx_Col3)) &lt;br/&gt;   -– The hint &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; necessary &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; overcome the Tipping &lt;br/&gt;   &lt;span class="hljs-comment"&gt;-- Point to produce a Bookmark Lookup &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   WHERE Col3 = 1 &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;END &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;GO &lt;/span&gt; 
Der Code in Listing 4 greift per Bookmark Lookup auf dieselbe Tabelle zu: im ersten Schritt lesend auf den Non-Clustered Index, im zweiten Schritt lesend auf den Clustered Index. Der Zugriff auf die beteiligten Ressourcen erfolgt auch hier nicht in der gleichen Reihenfolge, wodurch ein Deadlock ganz einfach provoziert werden kann!Der Transaction Isolation Level wurde in beiden Transaktionen auf Repeatable Read gesetzt, damit der Deadlock einfacher zu reproduzieren ist. In einer Produktivumgebung mit entsprechendem Workload lässt sich dieser Deadlock auch im Standard-Isolation-Level Read Committed reproduzieren. Bild 3 veranschaulicht diese Problematik.
Wie kann ein solcher Deadlock vermieden werden? Das Problem entsteht ja dadurch, dass der SQL Server intern in der falschen Reihenfolge auf die Ressourcen zugreift. Einfaches Umschreiben der Abfragen ist somit keine Option.Sie können den Deadlock jedoch verhindern, indem Sie den Bookmark Lookup ganz vermeiden – ein Covering Non-Clustered Index hilft dabei! Wird der lesende Zugriff auf den Clustered Index vermieden, lässt sich der Deadlock lösen. Listing 5 zeigt den dazu erforderlichen Index.
Listing 5: Covering Non-Clustered Index
-- Creates a Covering Non-Clustered Index &lt;br/&gt;CREATE NONCLUSTERED INDEX idx_Col3 ON Deadlock(&lt;span class="hljs-name"&gt;Col3&lt;/span&gt;) &lt;br/&gt;INCLUDE (&lt;span class="hljs-name"&gt;Col1&lt;/span&gt;, Col2) &lt;br/&gt;WITH (&lt;span class="hljs-name"&gt;DROP_EXISTING&lt;/span&gt; = ON) &lt;br/&gt;GO  
Bookmark Lookup Deadlocks sind folglich ein weiterer Grund, warum Sie Bookmark Lookups wirklich nur in Spezial­fällen in Kauf nehmen sollten.

Deadlocks aufgrund fehlender Indizes

Auch durch fehlende Indizes können Deadlocking-Situationen entstehen. Stellen Sie sich vor, Sie haben eine Heap Table­ (eine Tabelle ohne Clustered Index) und zusätzlich ist für diese Tabelle kein einziger Non-Clustered Index für den Datenzugriff definiert. In diesem Fall gibt es nur eine Möglichkeit, auf diese Tabelle zuzugreifen – nämlich über einen Table Scan Operator im Ausführungsplan.
Listing 6: Fehlende Indizes – Session 1
USE master &lt;br/&gt;GO&lt;br/&gt;-- &lt;span class="hljs-keyword"&gt;Create&lt;/span&gt; a &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; database &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; DATABASE DeadlockingDemo &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;-- Use it &lt;br/&gt;USE DeadlockingDemo &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;-- &lt;span class="hljs-keyword"&gt;Create&lt;/span&gt; a table without any indexes &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; TABLE Table1 ( &lt;br/&gt;   Column1 INT, Column2 INT ) &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;-- Insert a few records &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table1 VALUES (&lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table1 VALUES (&lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;span class="hljs-number"&gt;2&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table1 VALUES (&lt;span class="hljs-number"&gt;3&lt;/span&gt;, &lt;span class="hljs-number"&gt;3&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table1 VALUES (&lt;span class="hljs-number"&gt;4&lt;/span&gt;, &lt;span class="hljs-number"&gt;4&lt;/span&gt;) &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;-- &lt;span class="hljs-keyword"&gt;Create&lt;/span&gt; a table without any indexes &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; TABLE Table2 ( &lt;br/&gt;   Column1 INT, Column2 INT ) &lt;br/&gt;GO &lt;br/&gt;-- Insert a few records &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table2 VALUES (&lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table2 VALUES (&lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;span class="hljs-number"&gt;2&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table2 VALUES (&lt;span class="hljs-number"&gt;3&lt;/span&gt;, &lt;span class="hljs-number"&gt;3&lt;/span&gt;) &lt;br/&gt;INSERT &lt;span class="hljs-keyword"&gt;INTO&lt;/span&gt; Table2 VALUES (&lt;span class="hljs-number"&gt;4&lt;/span&gt;, &lt;span class="hljs-number"&gt;4&lt;/span&gt;) &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; TRANSACTION&lt;br/&gt;   -- Acquires an Exclusive Lock &lt;span class="hljs-keyword"&gt;on&lt;/span&gt; the row &lt;br/&gt;   UPDATE Table1 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; Column1 = &lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; Column2 = &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt;   -- Execute the query &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; Session &lt;span class="hljs-number"&gt;2&lt;/span&gt;... &lt;br/&gt;   -- ...&lt;br/&gt;&lt;br/&gt;   -- This query now requests a Shared Lock, but gets &lt;br/&gt;  -- blocked, because the other session/transaction &lt;br/&gt;  -- &lt;span class="hljs-keyword"&gt;has&lt;/span&gt; an Exclusive Lock &lt;span class="hljs-keyword"&gt;on&lt;/span&gt; one row, that &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; &lt;br/&gt;  -- currently updated &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; Column1 &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Table2 &lt;br/&gt;   --&lt;span class="hljs-keyword"&gt;WITH&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt; = idx_Column2) &lt;br/&gt;  -- The &lt;span class="hljs-keyword"&gt;index&lt;/span&gt; hint &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; necessary, because &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; this &lt;br/&gt;  -- tiny table, SQL Server will just scan the whole &lt;br/&gt;  -- table... &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; Column2 = &lt;span class="hljs-number"&gt;3&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;ROLLBACK TRANSACTION &lt;br/&gt;GO  
Dadurch müssen Sie bei jedem Zugriff auf die Tabelle die komplette Tabelle lesen (und für jeden Datensatz einen Shared Lock anfordern), auch wenn Sie nur an einem spezifischen Datensatz interessiert sind. Sehen Sie sich dazu Listing 6 und 7 näher an. Sie werden erkennen, dass hier auf die beiden beteiligten Tabellen wieder in unterschiedlicher Reihenfolge zugegriffen wird.
Listing 7: Fehlende Indizes – Session 2
-- Use the previous created database &lt;br/&gt;USE DeadlockingDemo &lt;br/&gt;GO &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; TRANSACTION &lt;br/&gt;   -- Acquires an Exclusive Lock &lt;span class="hljs-keyword"&gt;on&lt;/span&gt; the row &lt;br/&gt;   UPDATE Table2 &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; Column1 = &lt;span class="hljs-number"&gt;5&lt;/span&gt; &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; Column2 = &lt;span class="hljs-number"&gt;2&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; sys.dm_tran_locks &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; request_session_id = @@SPID &lt;br/&gt;&lt;br/&gt;   -- &lt;span class="hljs-keyword"&gt;Continue&lt;/span&gt; &lt;span class="hljs-keyword"&gt;with&lt;/span&gt; the query &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; Session &lt;span class="hljs-number"&gt;2&lt;/span&gt;... &lt;br/&gt;   -- ... &lt;br/&gt; &lt;br/&gt;   -- This query now requests a Shared Lock, but&lt;br/&gt;   -- gets blocked, because the other session/&lt;br/&gt;   -- transaction &lt;span class="hljs-keyword"&gt;has&lt;/span&gt; an Exclusive Lock &lt;span class="hljs-keyword"&gt;on&lt;/span&gt; one row, &lt;br/&gt;   -- that &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; currently updated &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; Column1 &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Table1 &lt;br/&gt;   -- &lt;span class="hljs-keyword"&gt;WITH&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt; = idx_Column2) -- The &lt;span class="hljs-keyword"&gt;index&lt;/span&gt; hint &lt;br/&gt;   -- &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; necessary, because &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; this tiny table SQL  &lt;br/&gt;   -- Server will just scan the whole table... &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; Column2 = &lt;span class="hljs-number"&gt;4&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;ROLLBACK TRANSACTION &lt;br/&gt;GO  
Dadurch entsteht, wie gehabt, ein Deadlock zwischen den beiden Transaktionen. Diesen Deadlock vermeiden Sie, indem Sie zusätzliche Non-Clustered Indizes für beide Tabellen definieren. Durch den Einsatz eines Non-Clustered Index kann der SQL Server über eine Seek-Operation die gewünschten Datensätze im Leaf Level des Non-Clustered Index finden, ohne auf die eigentliche Tabelle über einen Table Scan Operator zugreifen zu müssen. Und dadurch wird der Deadlock eliminiert. Listing 8 zeigt die dazu erforderlichen Non-Clustered Indizes.
Listing 8: Non-Clustered Indizes
-- &lt;span class="hljs-keyword"&gt;Create&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; indexes so that SQL Server &lt;span class="hljs-keyword"&gt;has&lt;/span&gt; alter&lt;br/&gt;-- native access paths &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; the data. The previous &lt;br/&gt;-- &lt;span class="hljs-number"&gt;2&lt;/span&gt; &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; statements can be now done through the &lt;br/&gt;-- Nonclustered &lt;span class="hljs-keyword"&gt;Index&lt;/span&gt; without acquiring a Shared &lt;br/&gt;-- Lock &lt;span class="hljs-keyword"&gt;on&lt;/span&gt; the table itself (which currently holds &lt;br/&gt;-- an Exclusive Lock &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; the UPDATE statement). &lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; NONCLUSTERED &lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt; idx_Column2 &lt;span class="hljs-keyword"&gt;ON&lt;/span&gt; Table1(Column2) &lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-keyword"&gt;CREATE&lt;/span&gt; NONCLUSTERED &lt;span class="hljs-keyword"&gt;INDEX&lt;/span&gt; idx_Column2 &lt;span class="hljs-keyword"&gt;ON&lt;/span&gt; Table2(Column2) &lt;br/&gt;GO  

Deadlocks beim Zugriff auf unterschiedliche Datenbereiche

Greifen Sie auf unterschiedliche Datenbereiche in unterschiedlicher Reihenfolge zu, droht Ihnen ebenfalls ein Deadlock. Hier ein konkretes Beispiel: Listing 9 und 10 zeigen wieder zwei Transaktionen, die sich gegenseitig blockieren können. Hier handelt es sich wieder um einen klassischen Cycle Deadlock, da auf unterschiedliche Datenbereiche in unterschiedlicher Reihenfolge zugegriffen wird. Auch dieser Deadlock lässt sich vermeiden, wenn Sie auf die Datenbereiche in identischer Reihenfolge zugreifen.
Listing 9: Deadlock – Session 1
USE AdventureWorks2014 &lt;br/&gt;GO &lt;br/&gt;&lt;br/&gt;-- Causes a deadlock when we access data &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; the &lt;br/&gt;-- wrong &lt;span class="hljs-keyword"&gt;order&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; TRANSACTION &lt;br/&gt;   -- &lt;span class="hljs-number"&gt;1&lt;/span&gt;st range &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; data &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;   UPDATE Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; FirstName = &lt;span class="hljs-string"&gt;'...'&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;br/&gt;   -- Switch &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; session &lt;span class="hljs-number"&gt;2&lt;/span&gt; &lt;br/&gt;   -- ...  &lt;br/&gt;   -- &lt;span class="hljs-number"&gt;2&lt;/span&gt;nd range &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; data &lt;br/&gt;   -- This statement will cause a deadlock! &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120209'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;   UPDATE Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; FirstName = &lt;span class="hljs-string"&gt;'...'&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120209'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;ROLLBACK &lt;br/&gt;GO  

Deadlocks bei Repeatable Read

Der Transaction Isolation Level Repetable Read ist ebenfalls sehr anfällig für Deadlocking-Situationen, da hier bekanntlich (siehe [1] [2]) die Shared Locks bis zum Ende der Transaktion gehalten werden, damit Repeatable Reads gewährleistet werden können. Sehen Sie sich dazu Listing 11 an.
Listing 10: Deadlock – Session 2
USE AdventureWorks2014 &lt;br/&gt;GO &lt;br/&gt;-- Causes a deadlock when we access data &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; the &lt;br/&gt;-- wrong &lt;span class="hljs-keyword"&gt;order&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; TRANSACTION &lt;br/&gt;   -- &lt;span class="hljs-number"&gt;2&lt;/span&gt;nd range &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; data &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120209'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;   UPDATE Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; FirstName = &lt;span class="hljs-string"&gt;'...'&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120209'&lt;/span&gt; &lt;br/&gt;   -- Switch &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; session &lt;span class="hljs-number"&gt;1&lt;/span&gt; &lt;br/&gt;   -- ... &lt;br/&gt;   -- &lt;span class="hljs-number"&gt;1&lt;/span&gt;st range &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; data &lt;br/&gt;   -- This statement will cause a deadlock! &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;   UPDATE Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; FirstName = &lt;span class="hljs-string"&gt;'...'&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;ROLLBACK &lt;br/&gt;GO  
Führen Sie diese Transaktion in zwei Sessions gleichzeitig aus, können Sie auch hier in einen Deadlock laufen. Das Problem dabei ist, dass keine der beiden Transaktionen den Exclusive Lock für das UPDATE-Statement anfordern kann, da bereits die andere Transaktion einen inkompatiblen Shared Lock auf den Datensatz angefordert hat. Dadurch blockieren beide UPDATE-Statements – Deadlock!
Listing 11: Deadlock in Repeatable Read – Session 1
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; AdventureWorks2014 &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;ISOLATION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;LEVEL&lt;/span&gt; REPEATABLE &lt;span class="hljs-keyword"&gt;READ&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;br/&gt;   &lt;span class="hljs-comment"&gt;-- Switch to session 2 &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   -- ... &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   -- This statement will cause a deadlock! &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   UPDATE Person.Person &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   SET FirstName = '...' &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   WHERE ModifiedDate = '20120208' &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;ROLLBACK &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;GO &lt;/span&gt; 
Diesen Deadlock lösen Sie auf, indem Sie zum Beispiel beim SELECT-Statement explizit einen Update Lock anfordern. Dieser ist kompatibel mit einem Shared Lock, aber inkompatibel mit sich selbst, und auch inkompatibel mit einem Exclusive Lock. Dadurch kann nur das erste SELECT-Statement ausgeführt werden. Das SELECT-Statement der anderen Session wird blockieren, da der Update Lock nicht angefordert werden kann.
Listing 12: Deadlock in Repeatable Read – Session 2
&lt;span class="hljs-keyword"&gt;USE&lt;/span&gt; AdventureWorks2014 &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;SET&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;ISOLATION&lt;/span&gt; &lt;span class="hljs-keyword"&gt;LEVEL&lt;/span&gt; REPEATABLE &lt;span class="hljs-keyword"&gt;READ&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;GO&lt;/span&gt; &lt;br/&gt; &lt;br/&gt;&lt;span class="hljs-keyword"&gt;BEGIN&lt;/span&gt; &lt;span class="hljs-keyword"&gt;TRANSACTION&lt;/span&gt; &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;SELECT&lt;/span&gt; * &lt;span class="hljs-keyword"&gt;FROM&lt;/span&gt; Person.Person &lt;br/&gt;   &lt;span class="hljs-keyword"&gt;WHERE&lt;/span&gt; ModifiedDate = &lt;span class="hljs-string"&gt;'20120208'&lt;/span&gt; &lt;span class="hljs-keyword"&gt;WITH&lt;/span&gt; (UPDLOCK) &lt;br/&gt;    &lt;span class="hljs-comment"&gt;-- Switch to session 1 &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   -- ... &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   -- This statement will cause a deadlock! &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   UPDATE Person.Person &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   SET FirstName = '...' &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;   WHERE ModifiedDate = '20120208' &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt; &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;ROLLBACK &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-comment"&gt;GO &lt;/span&gt; 
Dank dieser Vorgehensweise serialisiert der SQL Server beide Transaktionen und führt sie der Reihe nach aus. Die Performance sinkt dadurch zwar, aber als Gegenleistung haben Sie keinen Deadlock mehr, vergleiche Listing 12.

Intra-Parallelism Deadlocks

Ein äußerst gemeiner Deadlock im SQL Server ist der Intra-Parallelism Deadlock . Das Gemeine daran ist, dass es sich um einen Bug im SQL Server handelt, der bewusst nicht gefixt wird. Microsoft hat sich für diese Vorgehensweise entschieden, da die Behebung des Bugs zu risikoreich wäre. Wichtig ist für Sie zu erkennen, wann Sie es mit einem Intra-Parallelism Deadlock zu tun haben. Der Graph eines solchen Deadlocks verrät, dass es sich immer um die gleiche Session ID handelt, die Abfrage folglich selbst in einen Deadlock gelaufen ist. Dies ist möglich, weil ein Ausführungsplan eines solchen Deadlocks immer mit mehreren Worker-Threads umgesetzt wird, die schlussendlich gegenseitig aufeinander warten und dadurch den Deadlock verursachen.Trotzdem lässt sich ein solcher Deadlock vermeiden, denn ein Intra-Par­allelism Deadlock tritt ausschließlich in parallelen Ausführungsplänen auf. Sorgen Sie also dafür, dass solche Abfragen seriell mit nur einem Worker-Thread ausgeführt werden. Das klappt auf zweierlei Arten:
  • Anpassen der Indizierungsstrategie, damit sichergestellt werden kann, dass sich die Gesamtkosten des Ausführungsplanes unterhalb des Cost Threshold for Parallelism [4] bewegen.
  • Verwenden des Query Hints MAXDOP 1, da Sie dadurch einen seriellen Ausführungsplan erzwingen.

Read Committed Snapshot Isolation

In der vorigen Ausgabe der dotnetpro haben Sie Optimistic Concurrency und die beiden neuen Transaction Isolation Level Read Committed Snapshot Isolation und Snapshot Isolation kennengelernt [2].Die Grundidee hinter Optimistic Concurrency ist, dass lesende Vorgänge – also SELECT-Statements – keine Shared Locks mehr anfordern. Und dieses Verhalten kann Ihnen ebenfalls beim Eliminieren von Deadlocks helfen.Wie Sie auf den vorangegangenen Seiten gelesen haben, treten Deadlocks sehr oft zwischen lesenden und schreibenden Transaktionen auf. Wenn Sie bei lesenden Transaktionen die Shared Locks eliminieren, verhindern Sie zugleich die Deadlocks zwischen diesen beiden Datenzugriffsarten.Registrieren Sie daher sehr viele Deadlocks zwischen lesenden und schreibenden Transaktionen und haben auch keine direkte Möglichkeit, diese zu beheben, schalten Sie einfach mal Read Committed Snapshot Isolation für die betreffende Datenbank ein – das kann Wunder bewirken.

Fazit

Die wichtigste Nachricht dieses Artikels: Der SQL Server kann über seinen Deadlock Monitor Blockaden selbstständig erkennen und auflösen. Generell gilt, dass Sie möglichst immer in derselben Reihenfolge auf Tabellen und Daten zugreifen sollten. Dies verhindert viele Deadlocks bereits im Vorfeld. Kann diese Regel nicht befolgt werden, kann auch das Aktivieren von Read Committed Snapshot Isolation sehr viele Deadlocks beseitigen. 
Projektdateien

Fussnoten

  1. Klaus Aschenbrenner, SQL Server Concurrency, Teil 1, Pessimistic Concurrency, dotnetpro 2/2017, Seite 84 ff., http://www.dotnetpro.de/A1702SQLServer
  2. Klaus Aschenbrenner, SQL Server Concurrency, Teil 2, Optimistic Concurrency, dotnetpro 3/2017, Seite 80 ff., http://www.dotnetpro.de/A1703SQLServer
  3. Klaus Aschenbrenner, Nachschlagen mit Köpfchen, dotnetpro 3/2015, Seite 94 ff.,, http://www.dotnetpro.de/A1503TSQL
  4. Cost Threshold for Parallelism,, http://www.dotnetpro.de/SL1704SQLServer1

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige