Beim Clustering-Index neu erstellen. Warum wird die Datengröße verkleinert?

10

Wie kann dies geschehen, wenn wir einen Clustered-Index für eine Tabelle mit etwa 15 GB Daten neu erstellt und die Datengröße auf 5 GB verkleinert haben? Welche Art von "Daten" wird entfernt?

Datengröße Ich meine die "Daten" -Spalte von DBCC sp_spaceused

Vor dem erneuten Erstellen eines Clustered-Index:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    39169656 KB 15857960 KB 22916496 KB 395200 KB

Nach der Neuerstellung im Clustered-Index:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    29076736 KB 5867048 KB  22880144 KB 329544 KB

TSQL für den Wiederaufbau:

USE [DAX5TEST]
GO
ALTER INDEX [I_212RECID] ON [dbo].[LEDGERJOURNALTRANS] REBUILD PARTITION = ALL WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, ONLINE = ON, SORT_IN_TEMPDB = OFF, DATA_COMPRESSION = PAGE, FILLFACTOR = 85 )
GO
Daniel Björk
quelle
Bestimmen Sie die Datengröße aus der Dateigröße?
JNK
Datengröße Ich meine die "Daten" -Spalte von DBCC sp_spaceused
Daniel Björk
Das wäre die Spalte "Daten" von EXEC sp_spaceused.
RLF
1
Hat jeder Körper verpasst, dass OP in seinem Wiederherstellungsskript die Seitenkomprimierung = aktiviert verwendet, und ich denke, das war vorher nicht so. Daniel kannst du das bestätigen?
Shanky
1
@Shanky: Diese ALTER INDEXAnweisung scheint vom Code generiert worden zu sein (da sie eine Reihe von Optionen in ihrer Standardeinstellung enthält), daher vermute ich, dass sie aus den vorhandenen Optionen des Index erstellt wurde. Aber Sie haben Recht: Wenn die Komprimierung für den Clustered-Index vor dieser Ausführung nicht aktiviert war, würde dies definitiv den größten Teil der Reduzierung des Daten-Footprints erklären. (
Nochmals

Antworten:

16

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 11wü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 13und 17nicht 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 FILLFACTOROption 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 FILLFACTORwird 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 INDEXAnweisung, 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.

David Spillett
quelle
2
+1 Wenn der Seitenfüllfaktor weniger als 100% beträgt, z. B. wenn der Seitenfüllfaktor 50% beträgt, ist der neu erstellte Clustered-Index (die Tabelle ) doppelt so groß wie wenn er mit 100% Füllfaktor neu erstellt wurde.
Max Vernon
6

Wenn Sie einen Index neu erstellen, werden buchstäblich alle Daten auf neuen Seiten platziert. Was ich vermute, ist, dass Sie vor der Neuerstellung viele Daten entfernt haben, z. B. eine Spalte entfernt, eine Spalte mit variabler Breite auf weniger Daten aktualisiert, eine Spaltengröße mit fester Breite geändert oder viele Zeilen gelöscht haben. Bei beiden Vorgängen kann auf den Seiten viel Platz frei bleiben, der erst nach der Neuerstellung zurückgefordert wird. In der Spalte "Daten" werden sp_spaceusednicht die tatsächlichen Daten gemessen, sondern die Anzahl der 8 KB-Seiten, auf denen die Daten gespeichert sind. Diese Seiten sind jetzt aufgrund der Neuerstellung voller, sodass dieselbe Datenmenge auf eine kleinere Anzahl von Seiten passt.

Aaron Bertrand
quelle
5

Die sp_spaceusedgespeicherte Prozedur untersucht nicht die Gesamtgröße der Zeilen in der Datenbank. Es gibt die Größe des zugewiesenen Speicherplatzes für diese Daten in der kumulierten Größe der für die Daten zugewiesenen Speicherbereiche an.

Wenn ein erheblicher freier Speicherplatz verfügbar ist, z. B. aus vielen gelöschten Zeilen, würde eine Neuerstellung des Clustered-Index den Speicherplatz in Seiten und Bereichen komprimieren, um aus Leistungsgründen effizienter (dh kleiner) zu sein.

Es sollten also keine Daten verworfen worden sein, aber der Wiederherstellungsprozess machte den freien Speicherplatz, der in die Datenseiten eingebettet war, wieder verfügbar.

RLF
quelle