Überwinde MERGE JOIN (INDEX SCAN) mit einem expliziten einzelnen KEY-Wert auf einem FOREIGN KEY

9

Hinzugefügt 7/11 Das Problem ist, dass Deadlocks aufgrund von Index-Scans während MERGE JOIN auftreten. In diesem Fall setzt eine Transaktion, die versucht, eine S-Sperre für den gesamten Index in der übergeordneten FK-Tabelle zu erhalten, aber zuvor eine andere Transaktion eine X-Sperre für einen Schlüsselwert des Index.

Lassen Sie mich mit einem kleinen Beispiel beginnen (TSQL2012 DB von 70-461 Cource verwendet):

CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )

Spalten [custid], [empid], [shipperid]sind entsprechend korrelierte Parameter für [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]. In jedem Fall haben wir einen Clustered-Index für eine referenzierte Spalte in einer parrent-Tabelle.

ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])

Ich versuche, eine INSERT [Sales].[Orders] SELECT ... FROMandere Tabelle namens aufzurufen, [Sales].[OrdersCache]die die gleiche Struktur wie die [Sales].[Orders]außer Fremdschlüssel hat. Eine andere Sache könnte wichtig sein, um zu erwähnen, dass die Tabelle [Sales].[OrdersCache]ein Clustered-Index ist.

CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )

Wie erwartet, wenn ich versuche, ein geringes Datenvolumen einzufügen, funktioniert LOOP JOIN einwandfrei, sodass die Indexsuche für die Fremdschlüssel durchgeführt wird.

Bei hohen Datenmengen wird MERGE JOIN vom Abfrageoptimierer als effizienteste Methode zur Verwaltung des foregn-Schlüssels in der Abfrage verwendet.

Und es hat nichts damit zu tun, außer OPTION (LOOP JOIN) in unserem Fall mit Fremdschlüsseln oder INNER LOOP JOIN in expliziten JOIN-Fällen.

Unten ist die Abfrage, die ich in meiner Umgebung ausführen möchte:

INSERT Sales.Orders (
        custid, empid, shipperid, ... )
SELECT  custid, empid, 2, ...
FROM Sales.OrdersCache

Wenn wir uns den Plan ansehen, können wir sehen, dass alle 3 Foreing-Schlüssel mit MERGE JOIN validiert wurden. Dies ist für mich kein geeigneter Weg, da INDEX SCAN mit vollständiger Indexsperre verwendet wird. MERGE JOIN während der Fremdschlüsselüberprüfung

Die Verwendung von OPTION (LOOP JOIN) ist nicht geeignet, da sie fast 15% mehr kostet als MERGE JOIN (ich denke, die Regression wird mit zunehmendem Datenvolumen größer sein).

In der SELECT-Anweisung sehen Sie einen einzelnen Wert für das shipperidAttribut für die gesamte eingefügte Menge. Meiner Meinung nach muss es eine Möglichkeit geben, die Validierungsphase für den eingefügten Satz zumindest für das unveränderliche Attribut zu beschleunigen. Etwas wie:

  • Machen Sie LOOP JOIN, MERGE JOIN, HASH JOIN, wenn wir eine undefinierte Teilmenge für die JOIN-Validierung haben
  • Wenn es nur einen einzigen expliziten Wert für die validierte Spalte gibt, führen wir die Validierung nur einmal durch (INDEX SEEK).

Gibt es ein allgemeines Muster, um die oben genannte Situation mithilfe von Codestrukturen, zusätzlichen DDL-Objekten usw. zu überwinden?

Hinzugefügt 20/07. Lösung. Das Abfrageoptimierungsprogramm führt bereits mithilfe von MERGE JOIN eine Validierungsoptimierung für "Einzelschlüssel - Fremdschlüssel" durch. Dies gilt nur für die Tabelle "Sales.Shippers", wobei LOOP JOIN gleichzeitig für weitere Verknüpfungen in der Abfrage verbleibt. Da ich einige Zeilen in der übergeordneten Tabelle habe, verwendet Query Optimizer den Sort-Merge-Join-Algorithmus und vergleicht jede Zeile in der inneren Tabelle nur einmal mit der übergeordneten Tabelle. Das ist also die Antwort auf meine Frage, ob es einen bestimmten Mechanismus gibt, um einzelne Werte in einem Satz während der Einzelschlüsselvalidierung effektiv zu verarbeiten. Das ist keine so perfekte Entscheidung, aber so optimiert SQL Server den Fall.

Die Untersuchung der Leistungsbeeinträchtigung ergab, dass in meinem Fall die Einfügeanweisung MERGE JOIN und LOOP JOIN ungefähr gleich 750 gleichzeitig eingefügten Zeilen mit der folgenden Überlegenheit von MERGE JOIN (in der CPU-Zeitressource) wurde. Die Verwendung von OPTION (LOOP JOIN) ist daher eine geeignete Lösung für meinen Geschäftsprozess.

Oleg ich
quelle

Antworten:

8

Die Verwendung von OPTION (LOOP JOIN) ist nicht geeignet, da sie fast 15% mehr kostet als MERGE JOIN

Die in der Showplan-Ausgabe angezeigten Kostenprozentsätze sind immer Optimierungsmodellschätzungen, selbst in einem (tatsächlichen) Plan nach der Ausführung. Diese Kosten spiegeln wahrscheinlich nicht die tatsächliche Laufzeitleistung Ihrer speziellen Hardware wider. Der einzige Weg, um sicher zu sein, besteht darin, die Alternativen mit Ihrer Arbeitslast zu testen und die für Sie wichtigsten Metriken zu messen (verstrichene Zeit, CPU-Auslastung usw.).

Nach meiner Erfahrung wechselt der Optimierer viel zu früh von Loops Join zu Merge Join für die Fremdschlüsselüberprüfung. Bei allen außer den größten Operationen habe ich festgestellt, dass das Verbinden von Schleifen vorzuziehen ist. Und mit "groß" meine ich mindestens zig Millionen Zeilen, schon gar nicht die Tausenden, die Sie in den Kommentaren zu Ihrer Frage angeben.

Meiner Meinung nach muss es eine Möglichkeit geben, die Validierungsphase für den eingefügten Satz zumindest für das unveränderliche Attribut zu beschleunigen.

Dies ist im Prinzip sinnvoll, aber es gibt heute keine solche Logik innerhalb der Planerstellungslogik, die die Fremdschlüsselvalidierung verwendet. Die aktuelle Logik ist absichtlich sehr allgemein und orthogonal zur breiteren Abfrage. Spezifische Optimierungen erschweren das Testen und erhöhen die Wahrscheinlichkeit von Edge-Case-Fehlern.

Gibt es ein allgemeines Muster, um die oben genannte Situation mithilfe von Codestrukturen, zusätzlichen DDL-Objekten usw. zu überwinden?

Nicht dass ich wüsste. Das Deadlock-Risiko bei der Validierung von Merge-Join-Fremdschlüsseln ist allgemein bekannt . Die am häufigsten verwendete Problemumgehung ist der OPTION (LOOP JOIN)Hinweis. Es gibt keine Möglichkeit zu vermeiden, dass gemeinsam genutzte Sperren während der Fremdschlüsselüberprüfung verwendet werden, da diese für die Richtigkeit erforderlich sind , selbst bei Isolationsstufen für die Zeilenversionierung.

Es gibt keine bessere allgemeine Antwort (als den Loop-Join-Hinweis), wenn Sie die Möglichkeit für mehrere gleichzeitige Prozesse beibehalten möchten, Zeilen transaktional zu den übergeordneten und untergeordneten Tabellen hinzuzufügen. Wenn Sie jedoch bereit sind, die Datenänderungen zu serialisieren (nicht zu lesen), ist die Verwendung von sp_getapplock eine zuverlässige und einfache Technik.

Paul White 9
quelle