Gute Gründe für SELECT… WITH XLOCK?

11

Ich stehe vor einigen wiederkehrenden Deadlocks, von denen einer ein Keylock ist und eine SELECT-Abfrage mit XLOCK-Hinweis enthält, die zum Deadlock-Opfer wird. Die andere Anweisung ist ein INSERT in eine der Tabellen, die Teil der Ansicht der ersten Abfrage ist.

Aussicht:

create view dbo.viewE
 as
    select * from dbo.E  
    where myValue > 13000 

Wählen Sie Abfrage:

select * from dbo.viewE with (XLOCK) where A > GETUTCDATE() 

INSERT-Anweisung:

INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())

Die zugrunde liegende Tabelle dbo.E enthält ungefähr 3 Millionen Zeilen in ungefähr 20 Spalten, von denen einige ntext sind.

Wenn Sie die Abfragen herausnehmen und manuell mit zwei Transaktionen simulieren, ist das Verhalten reproduzierbar. Das Verhalten ändert sich, wenn XLOCK aus der Auswahl entfernt wird.

Deadlock-Grafik:

<deadlock-list>
 <deadlock victim="process222222221">
  <process-list>
   <process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="serializable (4)" xactid="27202256401" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="48" sqlhandle="0x02000000611e4523142b2318c47c87313a9b2ba587ff3130">
        SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()      </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@UICulture nvarchar(5))SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()    </inputbuf>
   </process>
   <process id="process6022222" taskpriority="0" logused="161152" waitresource="KEY: 5:72057604035644444 (cd874c2ba438)" waittime="1370" ownerId="27202248438" transactionguid="0x8de5ccd6eeef67469c6234af59e44ca5" transactionname="DTCXact" lasttranstarted="2015-09-14T16:32:34.767" XDES="0x4aa0bf950" lockMode="RangeI-N" schedulerid="14" kpid="6636" status="suspended" spid="329" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-09-14T16:32:37.300" lastbatchcompleted="2015-09-14T16:32:37.300" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="read uncommitted (1)" xactid="27202248438" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="936" sqlhandle="0x020000004853462f09790a4ddedc0d574c2afa539aef1c0e">
     INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock258b6dc80" mode="X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process6022222" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process222222221" mode="RangeX-X" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock7b145c400" mode="RangeX-X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process222222221" mode="RangeX-X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6022222" mode="RangeI-N" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

Soweit ich das verstehe, handelt es sich um einen KEYLOCK-Deadlock, der im Wesentlichen durch eine ungedeckte Indexabfrage verursacht wird, bei der ein nicht gruppierter und ein gruppierter Index verwendet werden, um die erforderlichen Werte zu erfassen, oder?

Meine Fragen:

  1. Ich kann aufgrund der erforderlichen NTEXT-Spalten keinen Deckungsindex erstellen. Hilft es hier, die Anzahl der Zeilen drastisch zu reduzieren?
  2. Gibt es einen guten Grund, warum ich nicht weiß, dass SELECT mit dem XLOCK ausgeführt wird? Würde der Deadlock auch ohne XLOCK passieren?
Magier
quelle

Antworten:

15

Soweit ich das verstehe, handelt es sich um einen KEYLOCK-Deadlock, der im Wesentlichen durch eine ungedeckte Indexabfrage verursacht wird, bei der ein nicht gruppierter und ein gruppierter Index verwendet werden, um die erforderlichen Werte zu erfassen, oder?

Im Wesentlichen ja. Die Leseoperation (Auswahl) greift zuerst auf den nicht gruppierten Index und dann auf den gruppierten Index (Suche) zu. Die Schreiboperation (Einfügen) greift zuerst auf den Clustered-Index und dann auf den Nonclustered-Index zu. Der Zugriff auf dieselben Ressourcen in einer anderen Reihenfolge mit inkompatiblen Sperren kann zu einem Deadlock führen.

Hilft es hier, die Anzahl der Zeilen drastisch zu reduzieren?

Es könnte , weil weniger Ressourcen gesperrt sind und der Betrieb wird dazu neigen , schneller zu vollenden . Wenn es hilft, kann es Deadlocks reduzieren , aber höchstwahrscheinlich nicht beseitigen (aber weiterlesen).

Gibt es einen guten Grund, warum ich nicht weiß, dass SELECT mit dem XLOCK ausgeführt wird?

Nicht wirklich. Solche Sperrhinweise werden häufig von Personen eingeführt, die nicht genau wissen, wie Isolation, Sperrung und Deadlocks funktionieren, um ein Problem zu reduzieren oder zu beseitigen.

Würde der Deadlock auch ohne XLOCK passieren?

Nein , wenn die Auswahl tatsächlich bei nicht festgeschriebener Leseisolation ausgeführt wird, da inkompatible Sperren nicht in einer anderen Reihenfolge genommen (und gehalten) werden.

Ja , wenn eine Sperrisolationsstufe verwendet wird und inkompatible Sperren in einer inkonsistenten Reihenfolge genommen und gehalten werden, z. B. gemeinsam genutzt (S) auf dem nicht gruppierten, dann S auf dem gruppierten beim Lesen. Wie wahrscheinlich ein Deadlock in diesem Szenario ist, hängt davon ab, wie viele Sperren verwendet werden und wie lange sie gehalten werden.

Rat

Das Besondere (bei der Überprüfung) ist, dass die ausgewählte Transaktion unter serialisierbarer Isolation ausgeführt wird . Dies kann durch Ihr Framework oder aufgrund der Verwendung des DTC (Distributed Transaction Coordinator) festgelegt werden - siehe Transaktionsname = "DTCXact" im Deadlock-Diagramm. Sie sollten die Gründe dafür untersuchen und versuchen, sie nach Möglichkeit zu ändern.

Ohne diese Eskalation zur Serialisierbarkeit stehen die Chancen sehr gut, dass dieser Deadlock nicht auftritt, vorausgesetzt, der XLOCKHinweis wird entfernt. Das heißt, Sie würden unter gelesener, nicht festgeschriebener Isolation lesen , was mit sehr wenigen Konsistenzgarantien einhergeht.

Wenn Ihre Anwendung und Ihr SQL Server-Code das Lesen von Zeilenversionen tolerieren können, würde das Ändern der RCSI (Commit Snapshot Isolation ) oder SI ( Snapshot Isolation ) für die Lesevorgänge auch den Deadlock vermeiden ( XLOCKentfernt!), Während ein konsistenter Point-In angezeigt wird -Zeitansicht der festgeschriebenen Daten. Dies setzt natürlich auch voraus, dass Sie die serialisierbare Isolation vermeiden können.

Letztlich ist die XLOCKAndeutung ist kontraproduktiv, aber Sie wirklich in den Grund für die Verwendung der serializable Isolationsstufe aussehen müssen. Das trancount = 2ist auch interessant - vielleicht verschachteln Sie hier ungewollt Transaktionen. Noch etwas zu überprüfen.

Paul White 9
quelle
2
  1. Wenn Sie die Anzahl der Zeilen drastisch reduzieren, verringert sich die Wahrscheinlichkeit, dass der Deadlock auftritt, aber er verschwindet nicht vollständig.

In einfachen Worten, die Auswahl verwendet zuerst den Index, um die auszuwählenden Zeilen zu bestimmen, ruft dann die Zeilen ab, während die Einfügung eine Zeile einfügt, und versucht dann, den (XLOCKED) -Index zu aktualisieren.

  1. Anwendungsentwickler verwenden normalerweise XLOCK, wenn sie in derselben Transaktion später ein Update der Daten durchführen möchten. Dies stellt sicher, dass niemand die Daten unter ihnen aktualisieren kann. Ich würde untersuchen, was die Anwendung tut, um festzustellen, ob XLOCK erforderlich ist.

Das Entfernen des XLOCK wird das Problem jedoch wahrscheinlich nicht beheben. Das SELECT hebt weiterhin eine gemeinsame Sperre für den Index auf, und das INSERT möchte, dass ein XLOCK ihn aktualisiert. Eine gemeinsam genutzte Sperre und ein XLOCK können für das Objekt nicht zusammen vorhanden sein, sodass Sie immer noch einen Deadlock erhalten. IX_Index1 muss entweder MyValue oder A oder beides sein.

Diese Art von Deadlock tritt häufig aufgrund schlecht gestalteter Indizes und / oder zu vieler Indizes auf. Oder schlecht geschriebener Code. Am besten prüfen Sie, ob die Auswahl auf irgendeine Weise neu geschrieben werden kann, um einen anderen Index zu verwenden.

Leo Miller
quelle