Gdy tabela ma indeks klastrowany, indeksem są dane tabeli (w przeciwnym razie masz tabelę typu sterty). Odbudowa indeksu klastrowego (w rzeczywistości dowolnego indeksu, ale miejsce nie byłoby liczone jako „dane” dla indeksu nieklastrowanego) spowoduje, że częściowo używane strony zostaną scalone w pełniejszą formę.
Podczas wstawiania danych do indeksu (klastrowego lub innego) w kolejności indeksu strony liści są tworzone zgodnie z potrzebami i zawsze będziesz mieć tylko jedną stronę częściową: tę na końcu. Gdy wprowadzasz dane poza kolejnością indeksu, strona musi zostać podzielona, aby dane zmieściły się we właściwym miejscu: w efekcie powstają dwie strony, które są w połowie zapełnione, a nowy wiersz przechodzi do jednej z nich. Z czasem może się to zdarzyć dużo, zużywając sporo dodatkowej przestrzeni, choć w pewnym stopniu przyszłe płytki wypełnią niektóre luki. Strony niebędące liśćmi również zobaczą podobny efekt, ale rzeczywiste strony danych mają znacznie większy rozmiar niż są.
Również usuwanie może powodować częściowe strony. Jeśli usuniesz wszystkie wiersze na stronie, zostanie to policzone jako „nieużywane”, ale jeśli pozostanie jeden lub więcej wierszy danych, nadal będzie liczone jako używane. Nawet jeśli na stronie jest tylko jeden wiersz zawierający 10 bajtów, ta strona liczy się jako 8192 bajtów w liczbie wykorzystanego miejsca. Ponownie przyszłe wstawki mogą wypełnić pewną lukę.
W przypadku wierszy o zmiennej długości aktualizacje mogą również mieć ten sam efekt: ponieważ wiersz staje się mniejszy, może pozostawić miejsce na swojej stronie, które później nie będzie łatwe do ponownego użycia, a jeśli wiersz na prawie pełnej stronie wydłuży się, może wymusić podział strony .
SQL Server nie spędza czasu na normalizowaniu danych przez przestawienie sposobu korzystania ze stron, dopóki nie zostanie to wyraźnie wskazane, na przykład kolejność odbudowywania indeksu, ponieważ takie ćwiczenia na śmiecie mogą być koszmarem wydajności.
Podejrzewam, że właśnie to widzisz, choć powiedziałbym, że przydzielenie wystarczającej ilości miejsca na ~ 2,7 razy więcej niż potrzeba danych jest szczególnie niekorzystnym przypadkiem. Może to sugerować, że masz coś losowego jako jeden ze znaczących kluczy w indeksie (być może kolumna UUID), co oznacza, że prawdopodobnie nie zostaną dodane nowe wiersze w kolejności indeksów i / lub że ostatnio nastąpiła znaczna liczba operacji usuwania.
Przykład podziału strony
Wstawianie w kolejności indeksu z wierszami o stałej długości, z których cztery pasują do strony:
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|__|__]
Teraz dodawanie wierszy poza kolejnością indeksu (dlatego użyłem liczb parzystych tylko powyżej): Dodanie 11
oznaczałoby albo wydłużenie drugiej strony (niemożliwe, ponieważ mają stały rozmiar), przeniesienie wszystkiego powyżej 11 w górę (zdecydowanie za drogie na duży indeks) lub podział strony w ten sposób:
[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]
Odtąd dodawanie 13
i 17
nie spowoduje podziału, ponieważ obecnie jest miejsce na odpowiednich stronach:
[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]
ale dodanie 03 spowoduje:
[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]
Jak widać, po tych operacjach wstawiania mamy obecnie przydzielonych 5 stron danych, które mogą zmieścić w sumie 20 wierszy, ale mamy tam tylko 14 wierszy („marnowanie” 30% miejsca).
Przebudowa z domyślnymi opcjami (patrz poniżej „współczynnik wypełnienia”) spowoduje:
[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]
zapisanie jednej strony w tym prostym przykładzie. Łatwo jest zobaczyć, jak skasowanie może mieć podobny efekt jak wstawianie poza kolejnością indeksowania.
Łagodzenie
Jeśli oczekujesz, że dane będą miały dość losową kolejność względem kolejności indeksów, możesz skorzystać z tej FILLFACTOR
opcji podczas tworzenia lub przebudowywania indeksu, aby powiedzieć SQL Serverowi, aby sztucznie pozostawiał luki do późniejszego wypełnienia - redukując podziały stron w dłuższej perspektywie, ale zajmuje początkowo więcej miejsca. Oczywiście błędne podanie tej wartości może znacznie pogorszyć sytuację, a nie poprawić sytuację, więc postępuj ostrożnie.
Podział strony, szczególnie w indeksie klastrowym, może mieć wpływ na wydajność wstawiania / aktualizacji, dlatego FILLFACTOR
czasami jest modyfikowany z tego powodu zamiast problemu z zajmowaniem miejsca w bazach danych, które wykazują dużą aktywność zapisu (ale w większości aplikacji, w których odczyty przeważają nad zapisami o kilka rzędów wielkości, na ogół lepiej jest pozostawić współczynnik wypełnienia na poziomie 100%, z wyjątkiem szczególnych przypadków, takich jak indeksy nad kolumnami z efektywnie losową zawartością).
Zakładam, że inne duże nazwiska DB mają podobną opcję, jeśli potrzebujesz w nich także tego poziomu kontroli.
Aktualizacja
Jeśli chodzi o ALTER INDEX
stwierdzenie dodane do pytania po tym, jak zacząłem pisać powyższe: Zakładam, że opcje są takie same, jak przy pierwszym budowaniu indeksu (lub ostatniej przebudowie), ale jeśli nie, to opcja kompresji może być bardzo znacząca, jeśli zostanie dodana czas dookoła. Również w tym stwierdzeniu współczynnik wypełnienia jest ustawiony na 85%, a nie 100%, więc każda strona liścia będzie ~ 15% pusta natychmiast po odbudowie.