Was bewirkt das Ersetzen von Indizes durch gefilterte Indizes (Werte ungleich Null)?

10

Unser Projekt betreibt eine sehr große, sehr komplizierte Datenbank. Vor ungefähr einem Monat haben wir festgestellt, dass der von indizierten Spalten mit Nullwerten verwendete Speicherplatz zu groß wurde. Als Antwort darauf schrieb ich ein Skript, das dynamisch alle einspaltigen Indizes durchsucht, die mehr als 1% der Nullwerte enthalten, und diese Indizes dann als gefilterte Indizes löscht und neu erstellt, sofern der Wert NICHT NULL ist. Dies würde Hunderte von Indizes in der gesamten Datenbank löschen und neu erstellen und in der Regel fast 15% des von der gesamten Datenbank verwendeten Speicherplatzes freigeben.

Jetzt habe ich zwei Fragen dazu:

A) Was sind die Nachteile der Verwendung gefilterter Indizes auf diese Weise? Ich würde davon ausgehen, dass dies nur die Leistung verbessern würde, aber gibt es Leistungsrisiken?

B) Beim Löschen und Neuerstellen der Indizes sind Fehler aufgetreten ( "Index XYZ kann nicht gelöscht werden, da er nicht vorhanden ist oder Sie keine Berechtigung haben" ), obwohl bei der anschließenden Überprüfung alles genau wie erwartet verlaufen ist. Wie kann das passieren?

Vielen Dank für jede Hilfe!

Bearbeiten: Als Antwort auf @Thomas Kejser

Hallo und danke, aber es stellte sich heraus, dass dies eine Katastrophe war. Zu der Zeit haben wir einige Dinge nicht verstanden wie:

  1. Während einer Abfrage erstellt SQLOS Indexpläne, bevor festgestellt wird, dass keine NULL-Werte zum Verknüpfen von Tabellenspalten verwendet werden können. IE, Sie müssen wirklich einen WHERE-Klauselfilter haben, der zum Index für jeden gefilterten Index passt, der in der Abfrage verwendet wird, oder der Index wird überhaupt nicht verwendet.
  2. Das Löschen und Erstellen von Indizes und das anschließende redundante Aktualisieren ihrer Statistiken reicht möglicherweise noch nicht aus, um die aktualisierten Pläne zu erstellen, von denen wir angenommen haben, dass sie dies tun würden. In einigen Fällen wird SQL Server aufgrund einer ausreichend hohen Arbeitslast gezwungen, die Pläne neu zu bewerten.
  3. Die Funktionalität des Ausführungsplaners weist einige Exoten auf, die allein durch gesunden Menschenverstand und Logik nur schwer zu bestimmen sind. Selbst mit Tausenden von Code-Behind-generierten Variationen verschiedener Abfragen können scheinbar nutzlose Indizes in einigen Statistiken und Abfrageplänen hilfreich sein, die letztendlich in kritischen Abfragen verwendet werden.

Am Ende wurden diese Änderungen rückgängig gemacht. Gefilterte Indizes sind also ein leistungsstarkes Werkzeug, aber Sie müssen wirklich genau verstehen, welche Daten aus diesen Spalten abgerufen werden. Wo normale Indizes neben den Speicherplatzproblemen eher einfach anzuwenden sind, stellen gefilterte Indizes sehr angepasste Lösungen dar. Sie sind sicherlich kein Ersatz für einen regulären Index, sondern eine Erweiterung für sie unter den besonderen Umständen, die sie benötigen.

Kahn
quelle
Möglicherweise möchten Sie auch Ihre Indizierungsstrategie überprüfen. Wenn Sie Hunderte von Einzelfeldindizes haben, ist dies wahrscheinlich nicht optimal.
JNK
Die Notwendigkeit hierfür ergibt sich aus der Tatsache, dass die Datenbank teilweise von einem anderen System geerbt wird. Standardmäßig haben wir einige abstrakte Tabellen und mehrere abstrakte Spalten, die möglicherweise überhaupt nicht verwendet werden, wodurch die meisten dieser massiven Mengen indizierter NULL-Werte erzeugt werden. Die Einzelfeldindizes werden aus der Grundanforderung erstellt, dass jeder Fremdschlüssel indiziert werden muss, und viele davon befinden sich in diesen Spalten, die meistens oder nur NULL-Werte enthalten.
Kahn

Antworten:

8

Sehr interessanter Ansatz. Mein Upvote für die Kreativität.

Da Sie den Speicherplatz zurückgefordert haben, gehe ich davon aus, dass die ursprünglichen Indizes nicht mehr vorhanden sind. Die Nachteile von gefilterten Indizes sind dann:

  • Zu viele davon können dazu führen, dass der Suchraum des Optimierers zu groß wird, was zu schlechten Abfrageplänen führt, wenn das Zeitlimit des Optimierers abläuft
  • Es gibt verschiedene Situationen, in denen ein gefilterter Index nicht einmal berücksichtigt wird, obwohl dies das nicht gefilterte Äquivalent wäre. Dies kann insbesondere dann der Fall sein, wenn Sie einen Hash-Join für die indizierte Spalte erhalten oder wenn Sie versuchen, nach der Spalte zu ordnen (ohne Filter).
  • Die Abfrageparametrierung funktioniert nicht mit gefilterten Indizes (siehe: http://www.sqlservercentral.com/blogs/practicalsqldba/2013/04/08/sql-server-part-9-filtered-index-a-new-way- for-performance-improvemnt / )

In der Praxis bedeutet dies, dass Sie mit gefilterten Indizes äußerst vorsichtig sein müssen, da diese häufig zu schrecklichen Abfrageplänen führen. Ich würde nicht so weit gehen, sie als nutzlos zu bezeichnen, aber ich betrachte sie als Ergänzung zu herkömmlichen Indizes, nicht als Ersatz (wie Sie es versuchen).

Thomas Kejser
quelle
"Die Abfrageparametrierung funktioniert nicht mit gefilterten Indizes". Dies kann wahrscheinlich mit Option (neu kompilieren) behoben werden
MichaelD
2

Thomas Kejser beantwortet dieses Thema weit oben.

Ich habe gerade darüber nachgedacht, 2 Cent hinzuzufügen.

Ich habe gesehen, dass einige gefilterte Indizes nur verwendet werden (im Ausführungsplan angezeigt), wenn Sie die where-Klausel in Ihrer Abfrage genau mit dem where im gefilterten Index übereinstimmen.

Haben Sie versucht, indizierte Ansichten zu verwenden ? spärliche Spalten ?

Ich glaube, dass Sie, sofern Sie nur innere Gelenke haben, eine indizierte Ansicht erstellen können, die die where-Klausel (n) Ihrer gefilterten Indizes enthält, und dann stattdessen die Ansicht verwenden können.

Es kann mehr als eine Ansicht geben. Aber genau wie bei den nicht gruppierten Indizes verlangsamen zu viele das Schreiben.

Nach meiner Erfahrung hätten Sie gute Lesegewinne, müssten jedoch Schreibvorgänge (Einfügungen und Aktualisierungen) überwachen, insbesondere wenn die Tabellen an der Replikation beteiligt sind.

Soweit ich jedoch Ihr Hauptanliegen verstehe, the null valueswürde ich Ihnen SPARSE-Spalten in Ihren Indizes vorschlagen .

Sparse-Spalten eignen sich besonders für gefilterte Indizes

Da ich spärliche Spalten beworben habe, würde ich mich nicht wohl fühlen, wenn ich Ihnen nicht auch über die Einschränkungen erzählen würde:

Beachten Sie beim Entwerfen von Tabellen mit spärlichen Spalten, dass beim Aktualisieren einer Zeile für jede spärliche Nicht-Null-Spalte in der Tabelle zusätzliche 2 Byte Overhead erforderlich sind.

Und folglich

Bei zusätzlichem Speicherbedarf können Aktualisierungen mit Fehler 576 unerwartet fehlschlagen, wenn die Gesamtzeilengröße einschließlich dieses Speicheraufwands 8019 überschreitet.

und es können keine Spalten aus der Zeile verschoben werden.

Betrachten Sie das Beispiel> einer Tabelle mit 600 spärlichen Spalten vom Typ bigint.

Wenn 571 Nicht-Null-Spalten vorhanden sind, beträgt die Gesamtgröße auf der Festplatte 571 * 12 = 6852 Byte. Nach Einbeziehung des zusätzlichen Zeilen-Overheads und des spärlichen Spaltenkopfs erhöht sich dieser auf ca. 6895 Byte. Auf der Seite sind noch ca. 1124 Byte auf der Festplatte verfügbar. Dies kann den Eindruck erwecken, dass zusätzliche Spalten erfolgreich aktualisiert werden können. Während des Updates entsteht jedoch ein zusätzlicher Overhead im Speicher, der 2 * beträgt (Anzahl der Spalten mit einer Dichte ungleich Null). In diesem Beispiel wird durch Einbeziehen des zusätzlichen Overheads - 2 * 571 = 1142 Byte - die Zeilengröße auf der Festplatte auf etwa 8037 Byte erhöht. Diese Größe überschreitet die maximal zulässige Größe von 8019 Byte. Da alle Spalten Datentypen mit fester Länge sind, können sie nicht aus der Zeile verschoben werden. Infolgedessen schlägt das Update mit dem Fehler 576 fehl.

Weitere Details zum obigen Link, ich möchte diese Warnung jedoch auch hier posten:

Das Ändern einer Spalte von "spärlich" in "nicht sparsam" oder "nicht sparsam" in "spärlich" erfordert das Ändern des Speicherformats der Spalte.

Das SQL Server-Datenbankmodul verwendet das folgende Verfahren, um diese Änderung durchzuführen:

1 - Fügt der Tabelle eine neue Spalte in der neuen Speichergröße und im neuen Format hinzu.

2 - Aktualisiert und kopiert für jede Zeile in der Tabelle den in der alten Spalte gespeicherten Wert in die neue Spalte.

3 - Entfernt die alte Spalte aus dem Tabellenschema.

4 - Erstellt die Tabelle neu (wenn kein Clustered-Index vorhanden ist) oder erstellt den Clustered-Index neu, um den von der alten Spalte verwendeten Speicherplatz zurückzugewinnen.

Marcello Miorelli
quelle
1
Hallo. Ein bisschen spät dran, aber ja, obwohl wir den in diesem Thema beschriebenen Ansatz vor langer Zeit aufgegeben haben, sind wir kürzlich mit einem selektiveren Ansatz darauf zurückgekommen. Grundsätzlich haben wir uns die Statistiknutzung und das Geschäftsmodell angesehen, um die Indizes auf Tabellenbasis zu bestätigen. Testen Sie es dann, indem Sie einen neuen gefilterten Index an der Seite des normalen Index hinzufügen, und überprüfen Sie über einige Wochen, welcher Index letztendlich verwendet wurde. Nachdem wir bestätigt haben, dass NUR die gefilterten Indizes in neuen Plänen verwendet wurden, haben wir die normalen nicht gefilterten gelöscht.
Kahn
1
Außerdem haben wir einige Spalten in spärliche Typen geändert. Das Problem dabei ist jedoch, dass, wie Sie in MSDN sehen werden, das Ändern eines Spaltentyps in "spärlich" im Wesentlichen die Neuerstellung des gesamten Clustered-Index erzwingt. Dies ist für große, komplexe Tische ziemlich schwer. Also haben wir die Einschränkungen und die Tabelle umbenannt, eine neue mit demselben Modell und ursprünglichem Namen, aber mit spärlichen Spalten erstellt und die Daten dann in geeigneten Stapeln in die neue Tabelle übertragen. Dann einmal überprüft, ob alles in Ordnung war und alle Indizes und FKs wieder vorhanden waren, löschte die alten Tabellen.
Kahn
1
In einigen Fällen war die Verwendung der Seitenkomprimierung weitaus vorzuziehen, sodass wir dies stattdessen taten. Dies ist auch praktisch, da Sie den vorhandenen Clustered-Index einfach mit DROP_EXISTING = ON erstellen können, um ihn weitaus schneller als den spärlichen Weg zu machen. Zumal es den ganzen Aufwand der erneuten Verwaltung von Indizes und FKs vermeidet.
Kahn