Database
 sql >> Datenbank >  >> RDS >> Database

USE HINT und DISABLE_OPTIMIZED_NESTED_LOOP

Einer der verfügbaren Algorithmen zum Verbinden zweier Tabellen in SQL Server ist Nested Loops. Der Nested-Loop-Join verwendet eine Join-Eingabe als äußere Eingabetabelle und eine als innere Eingabetabelle. Die äußere Schleife iteriert die äußere Eingabetabelle Zeile für Zeile. Die innere Schleife, die für jede äußere Zeile ausgeführt wird, sucht nach übereinstimmenden Zeilen in der inneren Eingabetabelle.

Dies wird als naiver Nested-Loop-Join bezeichnet.

Wenn Sie in der inneren Eingabetabelle einen Index für Join-Bedingungen haben, ist es nicht erforderlich, für jede Zeile der äußeren Tabelle eine innere Schleife auszuführen. Stattdessen können Sie den Wert aus der externen Tabelle als Suchargument übergeben und alle zurückgegebenen Zeilen der inneren Tabelle mit den Zeilen der äußeren Tabelle verbinden.

Die Suche durch die innere Tabelle ist ein wahlfreier Zugriff. Der SQL Server hat ab Version 2005 die Batch-Sort-Optimierung (nicht zu verwechseln mit dem Sort-Operator im Batch-Modus für Columnstore-Indizes). Der Zweck der Optimierung besteht darin, Suchschlüssel aus der externen Tabelle zu ordnen, bevor Daten aus der internen Tabelle abgerufen werden. Somit wird ein wahlfreier Zugriff sequentiell sein.

Der Ausführungsplan zeigt den Stapelsortiervorgang nicht als separaten Operator an. Stattdessen sehen Sie die Eigenschaft „Optimized=true“ im Operator „Nested Loops“. Wenn es möglich wäre, Stapelsortierung als separaten Operator im Plan zu sehen, würde dies wie folgt aussehen:

In diesem Pseudoplan lesen wir Daten aus dem nicht geclusterten ix_CustomerID-Index in der Reihenfolge dieses CustomerID-Indexschlüssels. Dann müssen wir eine Schlüsselsuche im Clustered-Index durchführen, da ix_CustomerID kein abdeckender Index ist. Key Lookup ist eine Suchoperation für Cluster-Indexschlüssel – ein wahlfreier Zugriff. Um es sequenziell zu machen, kann SQL Server eine Stapelsortierung nach dem Clustered-Index-Schlüssel ausführen.

Um mehr über Stapelsortierung zu erfahren, lesen Sie bitte meinen Artikel Stapelsortierung und verschachtelte Schleifen.

Diese Optimierung sorgt für einen großen Schub bei einer ausreichenden Anzahl von Zeilen. Weitere Informationen zu den Testergebnissen finden Sie im Blog OPTIMIZED Nested Loops Joins, erstellt von Craig Freedman, einem Optimierer-Entwickler.

Wenn die tatsächliche Anzahl der Zeilen jedoch geringer als die erwartete ist, können die zusätzlichen CPU-Kosten zum Erstellen dieser Sortierung ihre Vorteile verbergen, den CPU-Verbrauch erhöhen und die Leistung verringern.

Betrachten Sie dieses spezielle Beispiel:

use tempdb;
go
-- create a test table (SalesOrderID - clustered PK)
create table dbo.SalesOrder(SalesOrderID int identity primary key, CustomerID int not null, SomeData char(200) not null);
go
-- add test data
with n as (select top(1000000) rn = row_number() over(order by (select null)) from sys.all_columns c1,sys.all_columns c2)
insert dbo.SalesOrder(CustomerID, SomeData) select rn%500000, str(rn,100) from n;
-- create a clustered index
create index ix_c on dbo.Salesorder(CustomerID);
go
-- the batch sort optimization is enabled by default (Nested Loops: Optimized = true)
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000;
-- disable it with the DISABLE_OPTIMIZED_NESTED_LOOP hint (Nested Loops: Optimized = false)
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP'));
go

Das zurückgegebene Ergebnis:

Ich möchte Ihre Aufmerksamkeit auf die unterschiedliche Reihenfolge der Zeilen in der Ausgabe lenken. Der Server gibt Zeilen in der Reihenfolge zurück, in der er sie verarbeitet, da wir ORDER BY nicht explizit angegeben haben. Im ersten Fall lesen wir nach und nach aus dem ix_c-Index. Um jedoch zufällige Lesevorgänge aus dem gruppierten Index zu optimieren, filtern wir Zeilen nach dem gruppierten SalesOrderID-Indexschlüssel. Im zweiten Fall gibt es keine Sortierung, und Lesevorgänge werden in der CustomerID-Schlüsselreihenfolge des nicht gruppierten Indexes ix_c durchgeführt.

Unterschied zum Trace-Flag 2340

Obwohl die Dokumentation das Trace-Flag 2340 als Äquivalent zum Hinweis DISABLE_OPTIMIZED_NESTED_LOOP angibt, ist es nicht wirklich wahr.

Betrachten Sie das folgende Beispiel, in dem ich den undokumentierten Befehl UPDATE STATISTICS … WITH PAGECOUNT verwenden werde, um den Optimierer zu täuschen, indem ich sage, dass die Tabelle mehr Seiten benötigt, als sie tatsächlich hat. Sehen Sie sich dann diese Abfragen an:

  1. Ohne irgendwelche Hinweise (ich habe MAXDOP hinzugefügt, um einen einfachen Plan zu behalten);
  2. Mit dem Hinweis DISABLE_OPTIMIZED_NESTED_LOOP;
  3. Mit dem Trace-Flag 2340.
-- create a huge table
update statistics dbo.SalesOrder with pagecount = 100000;
go
set showplan_xml on;
go
-- 1. without hints
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(maxdop 1);
-- 2. hint
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP'), maxdop 1);
-- 3. trace flag
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(querytraceon 2340, maxdop 1);
go
set showplan_xml off;
go

Als Ergebnis haben wir folgende Pläne:

Nested Loops hat in allen drei Plänen die Eigenschaft Optimized =false. Tatsache ist, dass wir durch Erhöhen der Tabellenbreite auch die Datenzugriffskosten erhöhen. Wenn die Kosten hoch genug sind, verwendet SQL Server möglicherweise den expliziten Sortieroperator anstelle des impliziten Stapelsortieroperators. Wir können dies im ersten Abfrageplan sehen.

In der zweiten Abfrage haben wir den Hinweis DISABLE_OPTIMIZED_NESTED_LOOP verwendet, der die implizite Stapelsortierung deaktiviert. Allerdings entfällt eine explizite Sortierung durch einen separaten Operator.

Im dritten Plan können wir sehen, dass trotz Hinzufügen des Ablaufverfolgungsflags 2340 der Sortieroperator vorhanden ist.

Der Unterschied zwischen dem Hinweis und dem Flag ist also folgender:Ein Hinweis deaktiviert die Optimierung, indem er einen wahlfreien Zugriff in einen seriellen umwandelt, je nachdem, ob der Server ihn mit der impliziten Stapelsortierung oder mit dem separaten Sort-Operator implementiert.

P.S. Pläne können von der Ausstattung abhängen. Wenn Sie diese Abfragen nicht ausführen, versuchen Sie daher, die Spaltengröße SomeData char(200) in der Tabellenbeschreibung dbo.SalesOrder zu erhöhen oder zu verringern.

Der Artikel wurde vom Codingsight-Team mit Genehmigung des Autors übersetzt.