it-swarm.com.de

Wie wählt SQL Server die zu verwendenden Indizes aus?

Ich habe kürzlich begonnen, die verschiedenen Ansichten in unserer Datenbank zu indizieren. Einige dieser Tabellen werden häufig nur für Verknüpfungen verwendet, und selten werden sie in der Reihenfolge oder Reihenfolge nach Anweisungen verwendet. Dies bedeutet, dass wir mehrere Indizes (oder einen großen, relativ weniger effizienten Index) erstellen, die alle auf dem Primärschlüssel verschlüsselt sind. Ich sollte klarstellen, dass es kein Wo oder keine Reihenfolge gibt, indem ich etwas aus dieser Tabelle enthalte, und dass es in den Ansichten, mit denen ich Probleme habe, keine Schlüsselsuche gibt.

Wenn SQL mehrere Indizes hat, die alle auf tbl.ID verschlüsselt sind, wie trifft es dann die Wahl, einen Index über dem anderen zu verwenden? Ich finde, dass es zwischen den Ansichten variieren kann, auch wenn die aus dem Index gezogenen Daten gleich sind. Im Allgemeinen gibt es keinen großen Effizienzgewinn/-verlust bei der Auswahl eines Index gegenüber dem anderen. Dies lässt mich glauben, dass es einen "gut genug" -Index wählt und weitergeht (da viele aus derselben Spalte verschlüsselt sind).

Sollte aus diesem Grund das Erstellen mehrerer Indizes mit demselben Schlüssel vermieden werden?

Das spezifische Problem, mit dem ich mich gerade beschäftige, hat die Ansicht, einen Index wie diesen auszuwählen:

CREATE NONCLUSTERED INDEX [index1]
ON a.tbl ([ID])
INCLUDE (number,name,year, ...)

Wenn das Include 14 Spalten enthält (dies ist bedauerlich, aber aufgrund unserer aktuellen Abfragestruktur erforderlich).

Über den effizienteren Index:

CREATE NONCLUSTERED INDEX [index2]
ON a.tbl ([ID])
INCLUDE (number,name,year, ...)

Wo es nur 5 Spalten im Include gibt.

Beide Indizes enthalten die Informationen, die die Ansicht benötigt, aber einer ist offensichtlich viel kleiner und effizienter zu verwenden. Der größere Index muss vorhanden sein, um eine massive Ansicht abzudecken, die alle darin enthaltenen Spalten aufruft (ungefähr die Hälfte der Tabelle).

Es ist wahrscheinlich erwähnenswert, dass ich mich dafür entscheiden könnte, einen massiven Index wie oben zu erstellen, der fast jede Ansicht etwas weniger effizient abdeckt als diese kleineren Indizes. Aus meinen Tests geht hervor, dass das Hinzufügen dieser zusätzlichen Indizes keine signifikanten Kosten für die Aktualisierung der Tabellen verursacht. und so entschied ich mich, sie für die kleinen Leistungssteigerungen zu erstellen. Im Allgemeinen werden sie nach Bedarf verwendet, aber in bestimmten Fällen wählt der Optimierer ohne ersichtlichen Grund eine weniger effiziente Option.

7
Thomas D.

In meinem begrenzten Experimentieren:

  • Wenn die Abfrage einen Scan durchführen muss, führt SQL Server jedes Mal die Arbeit durch, um den engsten Index zu finden.
  • Wenn erwartet wird, dass die Abfrage eine Suche durchführt, ist die Indexbreite nicht wichtig, sondern es wird lediglich der zuletzt erstellte Deckungsindex verwendet.

Diese Art macht Sinn. Wenn Sie in beiden Fällen Regeln hinzufügen, um nach dem perfekten Index zu suchen, sollten Sie dies tun, wenn Sie dadurch am meisten gespart werden. Auf der Suche ist der Aufwand für die Suche nach einem besseren Index, wenn es nicht wirklich um Einsparungen geht für alle Zeilen im Gegensatz zu pro Zeile, weitaus weniger wahrscheinlich es ist es wert. "Last in" ist möglicherweise beabsichtigt, mit der Annahme, dass der zuletzt erstellte Deckungsindex wahrscheinlich der beste ist, den Sie erstellt haben, oder er kann nur willkürlich und zufällig sein (wie die Reihenfolge der Spalten in einer fehlenden Indexempfehlung).

Beweis (na ja, Art von Beweis)

Mein Testaufbau war ziemlich einfach:

CREATE TABLE dbo.t1
(
  id int IDENTITY(1,1) PRIMARY KEY,
  sn1 sysname, tn1 sysname, cn1 sysname, typ1 sysname,
  sn2 sysname, tn2 sysname, cn2 sysname, typ2 sysname,
  sn3 sysname, tn3 sysname, cn3 sysname, typ3 sysname,
  sn4 sysname, tn4 sysname, cn4 sysname, typ4 sysname
);
GO
SET NOCOUNT ON;
GO
INSERT dbo.t1
(
  sn1,tn1,cn1,typ1,sn2,tn2,cn2,typ2,
  sn3,tn3,cn3,typ3,sn4,tn4,cn4,typ4
)
SELECT s.name, t.name, c.name, typ.name,
       s.name, t.name, c.name, typ.name,
       s.name, t.name, c.name, typ.name,
       s.name, t.name, c.name, typ.name
FROM sys.schemas AS s
CROSS JOIN sys.objects AS t
INNER JOIN sys.all_columns AS c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.types AS typ
ON c.user_type_id = typ.user_type_id;
GO

CREATE VIEW dbo.v1
AS
  SELECT id,sn1,tn1
  FROM dbo.t1;
GO

Dadurch wurden 13.260 Zeilen in meine Tabelle aufgenommen (Ihre Ergebnisse variieren). Dann habe ich wiederholt die gleichen drei Indizes in unterschiedlicher Reihenfolge erstellt:

-- widest first
CREATE INDEX ix_wide ON dbo.t1(sn1) 
  INCLUDE(tn1,cn1,typ1,sn2,tn2,cn2,typ2,sn3,tn3,cn3,typ3,sn4,tn4,cn4,typ4);
GO
CREATE INDEX ix_mid ON dbo.t1(sn1) INCLUDE(tn1,cn1,sn2,tn2,cn2,typ2,sn3,tn3);
GO
CREATE INDEX ix_small ON dbo.t1(sn1) INCLUDE(tn1,cn1);
GO

-- widest last = ix_small then ix_mid then ix_wide
-- middle1 = ix_mid then ix_wide then ix_small
-- middle2 = ix_small then ix_wide then ix_mid

Dann habe ich in jedem dieser vier Fälle diese beiden Abfragen durchgeführt und die Pläne untersucht:

DBCC FREEPROCCACHE;
GO
SELECT id,sn1,tn1 FROM dbo.v1; -- scan
GO
SELECT id,sn1,tn1 FROM dbo.v1 WHERE sn1 LIKE N'q%'; -- seek

Ergebnisse:

         widest first  widest last  middle1   middle2
-------  ------------  -----------  --------  --------
   scan    ix_small      ix_small   ix_small  ix_small
   seek    ix_small      ix_wide    ix_small  ix_mid

Der Scan wählte immer den engsten Index. Bei der Suche wurde immer der zuletzt erstellte Index ausgewählt (da alle Deckblätter vorhanden sind). Ich habe die Tests nicht erweitert, um auch einen nicht abdeckenden Index in den Mix aufzunehmen, da ich nicht vermute, dass dies dieses Ergebnis ändern wird.

Moral (na ja, eine Art Moral)

Hier gibt es zwei Moralvorstellungen:

  1. Erstellen Sie zuerst Ihre breitesten Indizes, wenn Sie möchten, dass Ihre engsten Indizes bei Suchvorgängen verwendet werden, wenn dies möglich ist.
  2. Es ist wahrscheinlich eine enge Reihe von Anwendungsfällen, in denen sich diese Optimierung lohnt. Zum Beispiel Suchanfragen, die viele Zeilen zurückgeben nd Der breitere Deckungsindex hat sehr große Spalten, die nicht im engeren Index enthalten sind. In diesen Fällen kann es sinnvoll sein, den Index explizit mit einem Hinweis (und allen damit verbundenen Einschränkungen) anzugeben, anstatt sich auf das hier beobachtete Verhalten zu verlassen.

Wahrscheinlich fehlen mir hier einige Interna und Heuristiken, die Paul klären wird. Ich habe auch versucht, das Ablaufverfolgungsflag 302 für die Ausgabe von Indexauswahlinformationen zu aktivieren, aber dies scheint bei modernen Versionen von SQL Server nicht zu funktionieren.

9
Aaron Bertrand