Wenn eine Tabelle einen gruppierten Index hat, der Index ist die Datentabelle (sonst haben Sie eine Art Tabelle Heap). Eine Neuerstellung des Clustered-Index (tatsächlich ein Index, aber der Speicherplatz wird nicht als "Daten" für einen Nicht-Clustered-Index gezählt) führt dazu, dass teilweise verwendete Seiten zu einer vollständigeren Form zusammengeführt werden.
Wenn Sie Daten in Index (in Gruppen oder auf andere Weise) in Indexreihenfolge einfügen, werden nach Bedarf Blattseiten erstellt, und Sie haben immer nur eine Teilseite: die am Ende. Wenn Sie Daten außerhalb der Indexreihenfolge eingeben, muss eine Seite aufgeteilt werden, damit die Daten an die richtige Stelle passen: Sie erhalten zwei Seiten, die ungefähr halb voll sind, und die neue Zeile wird in eine davon eingefügt. Im Laufe der Zeit kann dies viel passieren und viel zusätzlichen Platz beanspruchen, obwohl zukünftige Einfügungen in gewissem Maße einige der Lücken füllen werden. Nicht-Blattseiten haben ebenfalls einen ähnlichen Effekt, aber die tatsächlichen Datenseiten sind weitaus bedeutender als sie.
Auch Löschvorgänge können zu Teilseiten führen. Wenn Sie alle Zeilen auf einer Seite entfernen, wird sie als "nicht verwendet" gezählt. Wenn jedoch eine oder mehrere Datenzeilen übrig sind, wird sie weiterhin als verwendet gezählt. Selbst wenn eine Seite nur eine Zeile mit 10 Byte enthält, zählt diese Seite als 8192 Byte in der Anzahl des verwendeten Speicherplatzes. Wiederum könnten zukünftige Beilagen einen Teil der Lücke füllen.
Bei Zeilen mit variabler Länge können Aktualisierungen auch den gleichen Effekt haben: Wenn eine Zeile kleiner wird, bleibt möglicherweise Platz auf ihrer Seite, der später nicht mehr einfach wiederverwendet werden kann. Wenn eine Zeile auf einer fast vollständigen Seite länger wird, kann dies zu einer Seitenteilung führen .
SQL Server verbringt keine Zeit damit, zu versuchen, die Daten zu normalisieren, indem die Verwendung der Seiten neu angeordnet wird, bis dies explizit angegeben wird, z. B. Ihre Reihenfolge für die Indexwiederherstellung, da solche Speicherbereinigungsübungen ein Leistungsalptraum sein können.
Ich vermute, dass dies das ist, was Sie sehen, obwohl ich sagen würde, dass es ein besonders schlimmer Fall ist, wenn genügend Speicherplatz für das 2,7-fache der Menge zugewiesen wird, die die Daten unbedingt benötigen. Dies kann bedeuten, dass Sie etwas Zufälliges als einen der signifikanten Schlüssel im Index haben (möglicherweise eine UUID-Spalte), was bedeutet, dass neue Zeilen wahrscheinlich nie in Indexreihenfolge hinzugefügt werden und / oder dass in letzter Zeit eine signifikante Anzahl von Löschvorgängen stattgefunden hat.
Beispiel für Seitenaufteilung
Einfügen in Indexreihenfolge mit Zeilen fester Länge, von denen vier in eine Seite passen:
Start with one empty page:
[__|__|__|__]
Add the first item in index order:
[00|__|__|__]
Add the next three
[00|02|04|06]
Adding the next will result in a new page:
[00|02|04|06] [08|__|__|__]
And so on...
[00|02|04|06] [08|10|12|14] [16|18|__|__]
Nun zum Hinzufügen von Zeilen außerhalb der Indexreihenfolge (aus diesem Grund habe ich nur die oben genannten geraden Zahlen verwendet): Das Hinzufügen 11
würde bedeuten, dass entweder die zweite Seite erweitert wird (nicht möglich, da sie eine feste Größe haben) und alles über 11 nach oben verschoben wird (viel zu teuer) einen großen Index) oder teilen Sie die Seite wie folgt auf:
[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]
Von hier aus führt das Hinzufügen 13
und 17
nicht zu einer Aufteilung, da derzeit Platz auf den entsprechenden Seiten vorhanden ist:
[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]
aber das Hinzufügen von 03 wird:
[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]
Wie Sie sehen können, sind nach diesen Einfügevorgängen derzeit 5 Datenseiten zugewiesen, die insgesamt 20 Zeilen aufnehmen können, aber wir haben dort nur 14 Zeilen ("Verschwendung" von 30% des Speicherplatzes).
Eine Neuerstellung mit Standardoptionen (siehe unten zum "Füllfaktor") würde Folgendes ergeben:
[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]
Speichern einer Seite in diesem einfachen Beispiel. Es ist leicht zu erkennen, wie Löschvorgänge einen ähnlichen Effekt haben können wie Einfügungen außerhalb der Indexreihenfolge.
Minderung
Wenn Sie erwarten, dass die Daten in Bezug auf die Indexreihenfolge in einer ziemlich zufälligen Reihenfolge vorliegen, können Sie die FILLFACTOR
Option beim Erstellen oder Neuerstellen eines Index verwenden, um SQL Server anzuweisen, künstlich Lücken zu lassen, die später ausgefüllt werden sollen anfangs mehr platz nehmen. Natürlich kann es falsch sein, diesen Wert falsch zu machen, anstatt die Situation zu verbessern. Gehen Sie also vorsichtig vor.
Das Aufteilen von Seiten, insbesondere im Clustered-Index, kann Auswirkungen auf die Leistung von Einfügungen / Aktualisierungen haben. Daher FILLFACTOR
wird es manchmal aus diesem Grund optimiert, anstatt das Problem der Speicherplatznutzung in Datenbanken zu verursachen, in denen viel Schreibaktivität auftritt (bei den meisten Apps jedoch, bei denen Lesevorgänge Schreibvorgänge überwiegen) Um mehrere Größenordnungen ist es im Allgemeinen besser, den Füllfaktor bei 100% zu belassen, außer in bestimmten Fällen, in denen Sie Indizes über Spalten mit effektiv zufälligem Inhalt haben.
Ich gehe davon aus, dass andere namhafte DBs eine ähnliche Option haben, wenn Sie diese Kontrolle auch in ihnen benötigen.
Aktualisieren
In Bezug auf die ALTER INDEX
Anweisung, die der Frage hinzugefügt wurde, nachdem ich mit der Eingabe der obigen Informationen begonnen habe: Ich gehe davon aus, dass die Optionen dieselben sind wie bei der ersten Erstellung (oder der letzten Neuerstellung) des Index, aber wenn nicht, kann die Komprimierungsoption sehr wichtig sein, wenn sie hinzugefügt wird Zeit um. Auch in dieser Anweisung ist der Füllfaktor auf 85% und nicht auf 100% festgelegt, sodass jede Blattseite unmittelbar nach der Neuerstellung zu ~ 15% leer ist.