Tablock-Hinweis löst Deadlocks aus

10

Ich habe zwei Datensätze mit minimaler Protokollierung in eine leere Heap-Tabelle eingefügt, indem ich zwei parallel und mit SQL der folgenden Form ausgeführte SQL-Aufgaben ausgeführt habe.

INSERT INTO Table (TABLOCK) SELECT FROM ...

Nachdem der Job etwas hängen geblieben war, wurde eine der SQL-Aufgaben zum Deadlock-Opfer. Unten finden Sie eine XML-Ausgabe des Deadlock-Diagramms.

Kann jemand erklären, was unter der Haube geschah?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

Die Dinge werden viel schwieriger, weil ich festgestellt habe, dass die beiden Execute SQL-Aufgaben in den meisten Fällen erfolgreich parallel ausgeführt werden können. Versuchen Sie es unten:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Da der einzige Unterschied die Anweisung SELECT ... FROM ... ist, kann die Anweisung SELECT ... FROM ... hier einen Einfluss auf den Sperrmodus haben?

SqlWhale
quelle
Sie können TABLOCKX anstelle von TABLOCK angeben, um den Deadlock zu verhindern. Obwohl dies auch den Zugriff auf die Tabelle serialisieren würde, würden Sie immer noch eine minimale Protokollierung erhalten.
Dan Guzman

Antworten:

8

Das Data Loading Performance Guide wurde für SQL Server 2008 geschrieben, aber soweit ich das beurteilen kann, hat Microsoft in diesem Bereich keine Verbesserungen für Heaps vorgenommen. Hier ist ein Angebot für Ihr Ladeszenario:

Massenladen einer leeren, nicht partitionierten Tabelle

Das Laden von Daten in eine nicht partitionierte Tabelle ist zwar ein einfacher Vorgang, kann jedoch auf verschiedene Arten optimiert werden.

...

Mehrere gleichzeitige Einfügevorgänge für Heaps sind nur möglich, wenn die ausgewählte Massenmethode BU-Sperren (Bulk Update) für die Tabelle ausgibt. Zwei BU-Sperren (Bulk Update) sind kompatibel, sodass zwei Bulk-Vorgänge gleichzeitig ausgeführt werden können.

In diesem Szenario haben sowohl INSERT… SELECT als auch SELECT INTO einen Nachteil. Beide Vorgänge erfordern eine exklusive (X) Sperre auf Tabellenebene für das Ziel. Dies bedeutet, dass zu einem bestimmten Zeitpunkt nur ein Massenladevorgang ausgeführt werden kann, was die Skalierbarkeit einschränkt. BCP, BULK INSERT und Integration Services können jedoch alle BU-Sperren (Bulk Update) verwenden, wenn Sie den TABLOCK-Hinweis angeben.

Der wichtige Teil ist, dass Sie keine BU-Sperre erhalten INSERT ... SELECT. Sie erhalten immer eine exklusive Sperre für den Tisch, sodass jeweils nur eine ausgeführt werden INSERTkann.

In den Kommentaren haben Sie angegeben, dass Sie 100.000 Zeilen oder weniger einfügen und dass während der Einfügungen keine anderen Prozesse in den Tabellen ausgeführt werden. Wenn ich zwei INSERT-Abfragen an die Datenbank sende, würde ich eines von drei Dingen erwarten:

  1. Ein Einsatz läuft zuerst und blockiert den anderen Einsatz. Die zweite Einfügung wartet, bis die erste Einfügung abgeschlossen ist.
  2. Ein Einsatz wird beendet, bevor der zweite Einsatz beginnt. Es gibt keine explizite Blockierung, aber sie werden nicht gleichzeitig ausgeführt.
  3. Sie erhalten einen Deadlock und nur eine Einfügung wird erfolgreich abgeschlossen.

In allen Fällen profitieren Sie entweder davon oder werden nicht verletzt, wenn Sie TABLOCKXder Abfrage einen Hinweis hinzufügen. Dies ist meine Empfehlung, den Deadlock zu umgehen. Wenn Sie wissen möchten, warum der Deadlock manchmal auftritt, müssen Sie nach einer anderen Antwort suchen.

In einem anderen Szenario, in dem Sie wirklich eine parallele Einfügung benötigen, können Sie das BU-Problem auf zwei Arten umgehen, indem Sie Ihren Heap partitionieren und jede Sitzung in eine separate Partition einfügen oder Ihre Daten über BCP, BULK INSERT oder Integration Services laden .

Joe Obbish
quelle
Vielen Dank für die Antwort, aber die Situation, in der ich auf einen Deadlock gestoßen bin, ist der einzige Fall, den ich jetzt habe. In den meisten Fällen schlägt der Job nicht fehl, wenn Sie INSERT INTO WITH (TABLOCK) SELECT FROM parallel ausgeben. Übrigens verwende ich SQL SERVER 2008 R2. Ich habe meiner Frage ein erfolgreiches Beispiel hinzugefügt.
SqlWhale
@tec in den Fällen, in denen es nicht fehlschlägt, laufen sie vermutlich tatsächlich seriell. Vielleicht treffen Sie das Problem nur, wenn es eine bestimmte Startzeit gibt, z. B. für die Erstellung von Plänen.
Martin Smith
@MartinSmith Die Abfrage, bei der ich auf das Problem im Projekt gestoßen bin, ist viel komplexer als das Beispiel SELECT 1, das mehr Kompilierungsaufwand erfordert, aber nur aus einigen Tabellen liest. Ich versuche, diese Art von Deadlock durch komplexere Abfragen zu reproduzieren.
SqlWhale
@TecKnowNothing Wie viele Zeilen fügen Sie ein? Wie oft pro Tag läuft der Prozess ab? WÄHLEN andere Abfragen aus der Tabelle, während Daten geladen werden?
Joe Obbish
@JoeObbish 1. Ich glaube nicht, dass die Anzahl der Zeilen hier ein Problem darstellt. Wir haben nur 4000 - 70000 für jede Abfrage, aber es handelt sich um hochaggregierte Daten für die Cube-Verwendung. 2. Es befindet sich noch in der Testphase und soll einmal pro Tag ausgeführt werden. 3. Nichts anderes liest aus der Zieltabelle.
SqlWhale
4

Sie einfügen in dbo.TargetTablezwei Sitzungen und beide mit TABLOCKhint.Both process9609dc8und process5e13048Prozesshalte Sch-Sund IXSchlösser , die miteinander kompatibel sind , so dass beide Verfahren gleichzeitig aufnehmen kann. Aber beide wollen IXSperre in Exclusive XTyp konvertieren . XSchlösser sind nicht miteinander kompatibel. Daher hat SQL Server eine der Sitzungen als Deadlock-Opfer ausgewählt, anstatt unendlich aufeinander zu warten.

Grundlegende Deadlocking-Informationen.

Diagramm zur Sperrkompatibilität (Database Engine).

Deadlocks erkennen und beenden.

SqlWorldWide
quelle