it-swarm.com.de

Der Index beschleunigt die Ausführung nicht und verlangsamt in einigen Fällen die Abfrage. Wieso ist es so?

Ich habe mit Indizes experimentiert, um die Dinge zu beschleunigen, aber im Falle eines Joins verbessert der Index die Ausführungszeit der Abfrage nicht und in einigen Fällen verlangsamt er die Dinge.

Die Abfrage zum Erstellen einer Testtabelle und zum Füllen mit Daten lautet:

CREATE TABLE [dbo].[IndexTestTable](
    [id] [int] IDENTITY(1,1) PRIMARY KEY,
    [Name] [nvarchar](20) NULL,
    [val1] [bigint] NULL,
    [val2] [bigint] NULL)

DECLARE @counter INT;
SET @counter = 1;

WHILE @counter < 500000
BEGIN
    INSERT INTO IndexTestTable
      (
        -- id -- this column value is auto-generated
        NAME,
        val1,
        val2
      )
    VALUES
      (
        'Name' + CAST((@counter % 100) AS NVARCHAR),
        Rand() * 10000,
        Rand() * 20000
      );

    SET @counter = @counter + 1;
END

-- Index in question
CREATE NONCLUSTERED INDEX [IndexA] ON [dbo].[IndexTestTable]
(
    [Name] ASC
)
INCLUDE (   [id],
    [val1],
    [val2])

Nun lautet Abfrage 1, die verbessert wurde (nur geringfügig, aber die Verbesserung ist konsistent):

SELECT *
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.ID = I2.ID
WHERE  I1.Name = 'Name1'

Statistiken und Ausführungsplan ohne Index (in diesem Fall verwendet die Tabelle den Standard-Clustered-Index):

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 5580, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 294 ms.

enter image description here

Jetzt mit aktiviertem Index:

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 2819, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 231 ms.

enter image description here

Nun die Abfrage, die aufgrund des Index langsamer wird (die Abfrage ist bedeutungslos, da sie nur zum Testen erstellt wurde):

SELECT I1.Name,
       SUM(I1.val1),
       SUM(I1.val2),
       MIN(I2.Name),
       SUM(I2.val1),
       SUM(I2.val2)
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.Name = I2.Name
WHERE   
       I2.Name = 'Name1'
GROUP BY
       I1.Name

Mit aktiviertem Clustered-Index:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 4, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 155106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17207 ms,  elapsed time = 17337 ms.

enter image description here

Jetzt mit deaktiviertem Index:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 5, logical reads 8642, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 165212, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17691 ms,  elapsed time = 9073 ms.

enter image description here

Die Fragen sind:

  1. Obwohl der Index vom SQL Server vorgeschlagen wird, warum verlangsamt er die Dinge um einen signifikanten Unterschied?
  2. Was ist der Nested-Loop-Join, der die meiste Zeit in Anspruch nimmt, und wie kann die Ausführungszeit verbessert werden?
  3. Gibt es etwas, das ich falsch mache oder verpasst habe?
  4. Wenn der Standardindex (nur für den Primärschlüssel) weniger Zeit in Anspruch nimmt und der nicht gruppierte Index für jede Zeile in der Verknüpfungstabelle vorhanden ist, sollte die Zeile für die verknüpfte Tabelle schneller gefunden werden, da sich die Verknüpfung in der Spalte Name befindet, in der Der Index wurde erstellt. Dies spiegelt sich im Abfrageausführungsplan wider und die Kosten für die Indexsuche sind geringer, wenn IndexA aktiv ist. Warum aber immer noch langsamer? Auch was ist in der linken äußeren Verknüpfung der verschachtelten Schleife, die die Verlangsamung verursacht?

Verwenden von SQL Server 2012

35
SpeedBirdNine

Obwohl der Index vom SQL Server vorgeschlagen wird, warum verlangsamt er die Dinge um einen signifikanten Unterschied?

Indexvorschläge werden vom Abfrageoptimierer gemacht. Wenn es auf eine logische Auswahl aus einer Tabelle stößt, die von einem vorhandenen Index nicht gut bedient wird, kann es seiner Ausgabe einen Vorschlag für einen "fehlenden Index" hinzufügen . Diese Vorschläge sind opportunistisch; Sie basieren nicht auf einer vollständigen Analyse der Abfrage und berücksichtigen keine umfassenderen Überlegungen. Bestenfalls sind sie ein Hinweis darauf, dass eine hilfreichere Indizierung möglich sein könnte, und ein erfahrener DBA sollte einen Blick darauf werfen.

Die andere Aussage zu fehlenden Indexvorschlägen ist, dass sie auf dem Kostenmodell des Optimierers basieren und der Optimierer schätzt, um wie viel der vorgeschlagene Index die geschätzten reduzieren könnte. -) Kosten der Abfrage. Die Schlüsselwörter hier sind "Modell" und "Schätzungen". Der Abfrageoptimierer weiß wenig über Ihre Hardwarekonfiguration oder andere Systemkonfigurationsoptionen - sein Modell basiert größtenteils auf festen Zahlen, die für die meisten Benutzer auf den meisten Systemen die meiste Zeit zu angemessenen Planergebnissen führen. Abgesehen von Problemen mit den genauen verwendeten Kostennummern sind die Ergebnisse immer Schätzungen - und Schätzungen können falsch sein.

Was ist der Nested-Loop-Join, der die meiste Zeit in Anspruch nimmt, und wie kann seine Ausführungszeit verbessert werden?

Es ist wenig zu tun, um die Leistung der Cross-Join-Operation selbst zu verbessern. Verschachtelte Schleifen sind die einzige physische Implementierung, die für einen Cross-Join möglich ist. Die Tabellenspule auf der Innenseite des Joins ist eine Optimierung, um zu vermeiden, dass die Innenseite für jede äußere Reihe erneut gescannt wird. Ob dies eine nützliche Leistungsoptimierung ist, hängt von verschiedenen Faktoren ab, aber in meinen Tests ist die Abfrage ohne sie besser dran. Dies ist wiederum eine Folge der Verwendung eines Kostenmodells - meine CPU und mein Speichersystem weisen wahrscheinlich andere Leistungsmerkmale auf als Ihre. Es gibt keinen spezifischen Abfragehinweis, um den Tabellen-Spool zu vermeiden, aber es gibt ein undokumentiertes Trace-Flag (8690), mit dem Sie die Ausführungsleistung mit und ohne Spool testen können. Wenn dies ein echtes Produktionssystemproblem wäre, könnte der Plan ohne Spule mithilfe einer Planführung erzwungen werden, die auf dem Plan basiert, der mit aktiviertem TF 8690 erstellt wurde. Die Verwendung von undokumentierten Trace-Flags in der Produktion wird nicht empfohlen, da die Installation technisch nicht mehr unterstützt wird und Trace-Flags unerwünschte Nebenwirkungen haben können.

Gibt es etwas, das ich falsch mache oder verpasst habe?

Die Hauptsache, die Sie vermissen, ist, dass der Plan, der den nicht gruppierten Index verwendet, gemäß dem Modell des Optimierers niedrigere geschätzte Kosten aufweist, jedoch ein erhebliches Problem mit der Ausführungszeit aufweist. Wenn Sie sich die Verteilung der Zeilen über die Threads im Plan mithilfe des Clustered-Index ansehen, werden Sie wahrscheinlich eine einigermaßen gute Verteilung feststellen:

Scan plan

In dem Plan, der die nicht gruppierte Indexsuche verwendet, wird die Arbeit vollständig von einem Thread ausgeführt:

Seek plan

Dies ist eine Folge der Art und Weise, wie die Arbeit durch parallele Scan-/Suchvorgänge auf die Threads verteilt wird. Es ist nicht immer so, dass ein paralleler Scan die Arbeit besser verteilt als eine Indexsuche - aber in diesem Fall. Komplexere Pläne könnten die Neuaufteilung des Austauschs umfassen, um die Arbeit über Threads hinweg neu zu verteilen. Dieser Plan hat keinen solchen Austausch. Sobald also Zeilen einem Thread zugewiesen wurden, werden alle zugehörigen Arbeiten an demselben Thread ausgeführt. Wenn Sie sich die Arbeitsverteilung für die anderen Operatoren im Ausführungsplan ansehen, werden Sie feststellen, dass alle Arbeiten von demselben Thread ausgeführt werden, der für die Indexsuche angezeigt wird.

Es gibt keine Abfragehinweise, die die Zeilenverteilung zwischen Threads beeinflussen könnten. Wichtig ist, dass Sie sich der Möglichkeit bewusst sind und genügend Details im Ausführungsplan lesen können, um festzustellen, wann ein Problem verursacht wird.

Mit dem Standardindex (nur für Primärschlüssel), warum dauert es weniger lange, und mit dem nicht gruppierten Index sollte für jede Zeile in der Verknüpfungstabelle die Zeile für die verknüpfte Tabelle schneller gefunden werden , da sich join in der Spalte Name befindet, für die der Index erstellt wurde. Dies spiegelt sich im Abfrageausführungsplan wider und die Kosten für die Indexsuche sind geringer, wenn IndexA aktiv ist. Warum aber immer noch langsamer? Was in der linken äußeren Verknüpfung der verschachtelten Schleife verursacht die Verlangsamung?

Es sollte jetzt klar sein, dass der Indexplan ohne Cluster potenziell effizienter ist, als Sie es erwarten würden. Es ist nur eine schlechte Verteilung der Arbeit auf die Threads zur Ausführungszeit, die das Leistungsproblem erklärt.

Um das Beispiel zu vervollständigen und einige der von mir erwähnten Dinge zu veranschaulichen, besteht eine Möglichkeit, eine bessere Arbeitsverteilung zu erzielen, darin, eine temporäre Tabelle zu verwenden, um die parallele Ausführung voranzutreiben:

SELECT
    val1,
    val2
INTO #Temp
FROM dbo.IndexTestTable AS ITT
WHERE Name = N'Name1';

SELECT 
    N'Name1',
    SUM(T.val1),
    SUM(T.val2),
    MIN(I2.Name),
    SUM(I2.val1),
    SUM(I2.val2)
FROM   #Temp AS T
CROSS JOIN IndexTestTable I2
WHERE
    I2.Name = 'Name1'
OPTION (FORCE ORDER, QUERYTRACEON 8690);

DROP TABLE #Temp;

Dies führt zu einem Plan, der die effizienteren Indexsuchen verwendet, keinen Tabellenspool enthält und die Arbeit gut auf die Threads verteilt:

Optimal plan

Auf meinem System wird dieser Plan erheblich schneller ausgeführt als die Clustered Index Scan-Version.

Wenn Sie mehr über die Interna der parallelen Abfrageausführung erfahren möchten, möchten Sie vielleicht siehe meine Sitzungsaufzeichnung für den PASS Summit 201 .

24
Paul White 9

Es ist nicht wirklich eine Frage des Index, es ist eher eine schlecht geschriebene Abfrage. Sie haben nur 100 eindeutige Werte für den Namen. Dies ergibt eine eindeutige Anzahl von 5000 pro Name.

Für jede Zeile in Tabelle 1 verbinden Sie also 5000 aus Tabelle 2. Können Sie 25020004 Zeilen sagen?.

Versuchen Sie dies, beachten Sie, dass dies nur 1 Index ist, den Sie aufgelistet haben.

    DECLARE @Distincts INT
    SET @Distincts = (SELECT  TOP 1 COUNT(*) FROM IndexTestTable I1 WHERE I1.Name = 'Name1' GROUP BY I1.Name)
    SELECT I1.Name
    , @Distincts
    , SUM(I1.val1) * @Distincts
    , SUM(I1.val2) * @Distincts
    , MIN(I2.Name)
    , SUM(I2.val1)
    , SUM(I2.val2)
    FROM   IndexTestTable I1
    LEFT OUTER JOIN

    (
        SELECT I2.Name
        , SUM(I2.val1) val1
        , SUM(I2.val2) val2
        FROM IndexTestTable I2
        GROUP BY I2.Name
    ) I2 ON  I1.Name = I2.Name
    WHERE I1.Name = 'Name1'
    GROUP BY  I1.Name

Und Zeit:

    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 8 ms.
    Table 'IndexTestTable'. Scan count 1, logical reads 31, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 1 ms.

    (1 row(s) affected)
    Table 'IndexTestTable'. Scan count 2, logical reads 62, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 10 ms.

enter image description here

Sie können SQL-Indizes nicht für schlecht geformte Abfragen verantwortlich machen

0
Adrian Sullivan