Jaki jest efekt zastąpienia indeksów indeksami filtrowanymi (o wartości innej niż null)?

10

Nasz projekt prowadzi bardzo dużą, bardzo skomplikowaną bazę danych. Około miesiąc temu zauważyliśmy, że przestrzeń wykorzystywana przez indeksowane kolumny zawierające wartości null robi się zbyt duża. W odpowiedzi napisałem jako skrypt, który dynamicznie przeszukiwałby wszystkie indeksy jednokolumnowe zawierające więcej niż 1% wartości pustych, a następnie upuszczał i odtwarzał te indeksy jako indeksy filtrowane pod warunkiem, że wartość NIE była NULL. Spowodowałoby to upuszczenie i odtworzenie setek indeksów w bazie danych i zwykle zwolniłoby prawie 15% miejsca używanego przez całą bazę danych.

Teraz mam dwa pytania na ten temat:

A) Jakie są wady korzystania z filtrowanych indeksów w ten sposób? Zakładam, że poprawiłoby to tylko wydajność, ale czy wiąże się to z jakimś ryzykiem?

B) Otrzymaliśmy błędy ( „nie można upuścić indeksu XYZ, ponieważ on nie istnieje lub nie masz uprawnień” ) podczas upuszczania i odtwarzania indeksów, nawet jeśli po sprawdzeniu wszystko poszło dokładnie tak, jak oczekiwano. Jak to się może stać?

Dzięki za wszelką pomoc!

Edycja: W odpowiedzi na @Thomas Kejser

Cześć i dzięki, ale okazuje się, że to była katastrofa. W tym czasie nie rozumieliśmy kilku rzeczy, takich jak:

  1. Podczas zapytania SQLOS tworzy plany indeksów przed ustaleniem, że nie może używać wartości NULL do łączenia kolumn tabeli. IE, naprawdę potrzebujesz filtru klauzuli WHERE dopasowującego indeks do każdego filtrowanego indeksu użytego w zapytaniu, w przeciwnym razie indeks nie będzie w ogóle używany.
  2. Upuszczanie i tworzenie indeksów oraz ponowne zbędne aktualizowanie ich statystyk może jeszcze nie wystarczyć do stworzenia zaktualizowanych planów, co zakładaliśmy, że tak. Wydaje się, że w niektórych przypadkach tylko wystarczająco duże obciążenie zmusi SQL Server do ponownej oceny planów.
  3. Istnieją pewne egzotyki dotyczące funkcjonalności narzędzia planowania wykonania, które są trudne do ustalenia wyłącznie na podstawie zdrowego rozsądku i logiki. Z tysiącami wygenerowanych przez kod odmian różnych zapytań, pozornie bezużyteczne indeksy mogą pomóc w niektórych statystykach i planach zapytań, które ostatecznie są wykorzystywane w zapytaniach krytycznych.

Ostatecznie zmiany te zostały cofnięte. Filtrowane indeksy są więc potężnym narzędziem, ale trzeba naprawdę dokładnie zrozumieć, jakie dane są pobierane z tych kolumn. Tam, gdzie normalne indeksy oprócz problemów z przestrzenią są raczej łatwe do zastosowania, indeksy filtrowane reprezentują rozwiązania bardzo dostosowane. Z pewnością nie zastępują one zwykłego indeksu, a raczej rozszerzenie go w tych szczególnych okolicznościach, które są wymagane.

Kahn
źródło
Możesz także ponownie sprawdzić swoją strategię indeksowania. Jeśli masz setki indeksów pojedynczych pól, prawdopodobnie nie jest to optymalne.
JNK
Potrzeba ich wynika z faktu, że baza danych jest częściowo dziedziczona z innego systemu. Domyślnie mamy kilka tabel abstrakcyjnych i kilka kolumn abstrakcyjnych, które w ogóle mogą nie być używane, co powoduje powstanie większości tych ogromnych ilości indeksowanych wartości NULL. Jeśli chodzi o indeksy pojedynczego pola, są one tworzone na podstawie podstawowego wymogu indeksowania każdego klucza obcego, a wiele z nich znajduje się w tych kolumnach, które zawierają głównie lub tylko wartości NULL.
Kahn

Odpowiedzi:

8

Bardzo ciekawe podejście. Moje poparcie dla kreatywności.

Ponieważ odzyskałeś miejsce, zakładam, że oryginalne indeksy nie są już na miejscu? Wady filtrowanych indeksów to:

  • Zbyt wiele z nich może powodować, że przestrzeń wyszukiwania optymalizatora będzie zbyt duża, co prowadzi do złych planów zapytań, ponieważ optymalizator wygasa
  • Istnieje kilka sytuacji, w których filtrowany indeks nie będzie nawet brany pod uwagę, nawet jeśli niefiltrowanym odpowiednikiem byłby. W szczególności może się to zdarzyć, gdy uzyskasz sprzężenie mieszające w indeksowanej kolumnie lub jeśli spróbujesz ORDER BY według kolumny (bez filtra)
  • Parametryzacja zapytania nie działa z filtrowanymi indeksami (patrz: http://www.sqlservercentral.com/blogs/practicalsqldba/2013/04/08/sql-server-part-9-filtered-index-a-new-way- for-performance-Improvemnt / )

W praktyce oznacza to, że musisz być bardzo ostrożny z filtrowanymi indeksami, ponieważ często skutkują strasznymi planami zapytań. Nie posunąłbym się nawet do nazwania ich bezużytecznymi, ale widzę je jako dodatek do tradycyjnych indeksów, a nie jako zamiennik (jak próbujesz to zrobić).

Thomas Kejser
źródło
„Parametryzacja zapytania nie działa z filtrowanymi indeksami”. można to prawdopodobnie naprawić za pomocą opcji (rekompilacja)
MichaelD
2

Thomas Kejser odpowiada na ten temat znacznie powyżej.

Właśnie pomyślałem o dodaniu 2 centów.

Widziałem, że niektóre filtrowane indeksy są używane (pokazane w planie wykonania) tylko wtedy, gdy dokładnie pasujesz do klauzuli where w zapytaniu jako gdzie w filtrowanym indeksie.

próbowałeś użyć widoków indeksowanych ? rzadkie kolumny ?

Uważam, że o ile masz tylko połączenia wewnętrzne, możesz utworzyć widok indeksowany zawierający klauzulę where swoich odfiltrowanych indeksów, a następnie możesz użyć tego widoku.

Może być więcej niż jeden widok. Ale podobnie jak w przypadku indeksów nieklastrowanych, zbyt wiele spowalnia zapisywanie.

Z mojego doświadczenia wynika, że ​​zyskasz na czytaniu, ale będziesz musiał monitorować zapisy (wstawki i aktualizacje), szczególnie jeśli tabele są zaangażowane w replikację.

Jednak, jak rozumiem, twoimi głównymi obawami są the null valuesdlatego sugeruję SPARSE kolumny w twoich indeksach .

Rzadkie kolumny są szczególnie odpowiednie dla przefiltrowanych indeksów

Ponieważ reklamowałem rzadkie kolumny, nie czułbym się dobrze, gdybym nie powiedział wam również o jego ograniczeniach:

Projektując tabele z rzadkimi kolumnami, należy pamiętać, że dodatkowe 2 bajty narzutu są wymagane dla każdej niezerowej rzadkiej kolumny w tabeli podczas aktualizacji wiersza.

W wyniku tego

dodatkowe zapotrzebowanie na pamięć, aktualizacje mogą się nieoczekiwanie zawieść z błędem 576, gdy całkowity rozmiar wiersza, w tym narzut pamięci, przekracza 8019,

i nie można wypchnąć żadnych kolumn z wiersza.

Rozważ przykład> tabeli, która ma 600 rzadkich kolumn typu bigint.

Jeśli istnieje 571 niepustych kolumn, całkowity rozmiar dysku wynosi 571 * 12 = 6852 bajtów. Po dołączeniu dodatkowego narzutu wiersza i rzadkiego nagłówka kolumny zwiększa się to do około 6895 bajtów. Strona wciąż ma około 1124 bajtów dostępnych na dysku. Może to sprawiać wrażenie, że dodatkowe kolumny można pomyślnie zaktualizować. Jednak podczas aktualizacji występuje dodatkowy narzut w pamięci, który wynosi 2 * (liczba niezerowych rzadkich kolumn). W tym przykładzie, włączając dodatkowe obciążenie - 2 * 571 = 1142 bajtów - zwiększa rozmiar wiersza na dysku do około 8037 bajtów. Rozmiar ten przekracza maksymalny dozwolony rozmiar 8019 bajtów. Ponieważ wszystkie kolumny są typami danych o stałej długości, nie można ich wypchnąć z wiersza. W rezultacie aktualizacja kończy się niepowodzeniem z błędem 576.

więcej szczegółów na temat powyższego linku, jednak wolę opublikować tutaj również to ostrzeżenie:

Zmiana kolumny z rzadkiej na rzadką lub nielicznej na rzadką wymaga zmiany formatu przechowywania kolumny.

Aparat baz danych programu SQL Server stosuje następującą procedurę w celu wprowadzenia tej zmiany:

1 - Dodaje nową kolumnę do tabeli w nowym rozmiarze i formacie pamięci.

2 - Dla każdego wiersza w tabeli aktualizuje i kopiuje wartość zapisaną w starej kolumnie do nowej kolumny.

3 - Usuwa starą kolumnę ze schematu tabeli.

4 - Odbudowuje tabelę (jeśli nie ma indeksu klastrowanego) lub odbudowuje indeks klastrowany w celu odzyskania miejsca używanego przez starą kolumnę.

Marcello Miorelli
źródło
1
Cześć. Trochę późno do walki, ale tak, choć dawno temu porzuciliśmy podejście opisane w tym temacie, ostatnio wróciliśmy do niego z bardziej selektywnym podejściem. Zasadniczo przyjrzeliśmy się wykorzystaniu statystyk i modelowi biznesowemu, aby potwierdzić indeksy dla poszczególnych tabel. Następnie przetestowałem go, dodając nowy filtrowany indeks z boku normalnego indeksu i sprawdziłem, czy w ciągu kilku tygodni zobaczyłem, który z nich został użyty. Po potwierdzeniu, że TYLKO przefiltrowane indeksy były używane w nowych planach, porzuciliśmy normalne niefiltrowane.
Kahn
1
Zmieniliśmy też sporo kolumn na rzadkie. Problem polega na tym, że jak widać z MSDN, zmiana typu kolumny na rzadką zasadniczo wymusza odtworzenie całego indeksu klastrowego. Czyni to raczej ciężkim dla dużych, złożonych tabel. Dlatego zmieniliśmy nazwę ograniczeń i tabeli, utworzyliśmy nową z tym samym modelem i oryginalną nazwą, ale z rzadkimi kolumnami, a następnie przenieśliśmy dane do nowej tabeli w odpowiednich partiach. Potem sprawdziłem, czy wszystko jest w porządku, a wszystkie indeksy i FK znów były na miejscu, porzuciłem stare tabele.
Kahn
1
Ponadto w niektórych przypadkach kompresja stron była zdecydowanie lepsza, więc zamiast tego zrobiliśmy to. Jest to również przydatne, ponieważ można po prostu utworzyć istniejący indeks klastrowy za pomocą DROP_EXISTING = ON, aby uczynić go daleko, znacznie szybszym niż wybranie rzadkiej trasy. Zwłaszcza, że ​​pozwala to uniknąć kłopotów związanych z ponownym zarządzaniem indeksami i FK.
Kahn