Czy powinienem indeksować pole bitowe w programie SQL Server?

99

Pamiętam, jak kiedyś przeczytałem, że indeksowanie pola o niskiej liczności (mała liczba odrębnych wartości) nie jest naprawdę warte wykonywania. Przyznaję, że nie wiem wystarczająco dużo o działaniu indeksów, aby zrozumieć, dlaczego tak jest.

A co, jeśli mam tabelę ze 100 milionami wierszy i wybieram rekordy, w których pole bitowe ma wartość 1? Powiedzmy, że w dowolnym momencie istnieje tylko kilka rekordów, w których pole bitowe ma wartość 1 (w przeciwieństwie do 0). Czy warto indeksować to pole bitowe, czy nie? Czemu?

Oczywiście mogę to po prostu przetestować i sprawdzić plan wykonania i zrobię to, ale jestem też ciekawa teorii, która za tym stoi. Kiedy liczność ma znaczenie, a kiedy nie?

jeremcc
źródło
Czy to typowe zapytanie? Może się to opłacać, szukając „garści” płyt, ale nie pomoże ci zbytnio w innych wierszach. Czy istnieją inne sposoby identyfikacji danych?
jason saldo
4
Chociaż nie sądzę, żebym indeksował TYLKO kolumnę bitową samodzielnie, bardzo często dołącza się kolumny bitowe jako część indeksu złożonego. Prostym przykładem może być indeks ACTIVE, LASTNAME zamiast tylko nazwiska, gdy Twoja aplikacja prawie zawsze szuka aktywnych klientów.
BradC
„Pamiętam, jak kiedyś przeczytałem, że indeksowanie pola o niskiej liczności (z małą liczbą odrębnych wartości) nie jest naprawdę warte robienia”. To dlatego, że SQL Server prawie zawsze okaże się bardziej wydajne, aby po prostu wykonać skanowanie tabeli niż odczytać indeks. Więc w zasadzie twój indeks nigdy nie zostanie użyty i szkoda go utrzymywać. Jak powiedzieli inni, może to być w porządku w indeksie złożonym.
DJ.
5
Nie zgodziłbym się. Jeśli twoja dystrybucja to 50/50, nigdy nie użyjesz indeksu, ponieważ szybsze byłoby wykonanie skanowania tabeli. Jeśli jednak masz tylko 5, 1 wartości i 1 milion 0 wartości, najprawdopodobniej użyjesz indeksu podczas wyszukiwania 1.
Kibbee,
1
W podanym przykładzie byłbym bardziej skłonny umieścić LastName na pierwszym miejscu. Zależy to od obciążenia określonego zapytania, ale generalnie posiadanie najpierw bardziej selektywnej kolumny oznacza, że ​​indeks jest bardziej prawdopodobny.
Mitch Wheat,

Odpowiedzi:

72

Zastanów się, czym jest indeks w SQL - a indeks jest tak naprawdę kawałkiem pamięci wskazującym na inne fragmenty pamięci (tj. Wskaźniki do wierszy). Indeks jest podzielony na strony, dzięki czemu części indeksu mogą być ładowane i usuwane z pamięci w zależności od użycia.

Kiedy pytasz o zestaw wierszy, SQL używa indeksu, aby znaleźć wiersze szybciej niż skanowanie tabeli (przeglądanie każdego wiersza).

SQL ma indeksy klastrowe i nieklastrowe. Rozumiem, że indeksy klastrowe grupują podobne wartości indeksów na tej samej stronie. W ten sposób, gdy pytasz o wszystkie wiersze pasujące do wartości indeksu, SQL może zwrócić te wiersze ze zgrupowanej strony pamięci. Dlatego próba grupowania indeksowania kolumny GUID jest złym pomysłem - nie próbujesz grupować losowych wartości.

Podczas indeksowania kolumny zawierającej liczby całkowite indeks SQL zawiera zestaw wierszy dla każdej wartości indeksu. Jeśli masz zakres od 1 do 10, masz 10 wskaźników indeksu. W zależności od liczby wierszy może to być stronicowane inaczej. Jeśli zapytanie szuka indeksu pasującego do „1”, a następnie tam, gdzie nazwa zawiera „Fred” (zakładając, że kolumna Nazwa nie jest indeksowana), SQL bardzo szybko pobiera zestaw wierszy pasujących do „1”, a następnie tabela przeszukuje tabelę w celu znalezienia reszty.

Więc to, co naprawdę robi SQL, to próba zmniejszenia zestawu roboczego (liczby wierszy), przez który musi iterować.

Indeksując pole bitowe (lub jakiś wąski zakres), redukujesz zestaw roboczy tylko o liczbę wierszy pasujących do tej wartości. Jeśli masz małą liczbę pasujących wierszy, znacznie zmniejszyłoby to zestaw roboczy. W przypadku dużej liczby wierszy z rozkładem 50/50 może to przynieść niewielki wzrost wydajności w porównaniu z utrzymaniem aktualności indeksu.

Powodem, dla którego wszyscy mówią, aby testować, jest to, że SQL zawiera bardzo sprytny i złożony optymalizator, który może zignorować indeks, jeśli zdecyduje, że skanowanie tabeli jest szybsze, może użyć sortowania, lub może organizować strony pamięci, jak cholernie lubi.

Geoff Cox
źródło
Wygląda więc na to, że gdybym miał tylko kilka wierszy, w których pole bitu ma wartość 1 (na przykład śledzenie „IsProcessed”), to indeks byłby dobry, ponieważ uporządkuje je według wartości, a następnie będzie mógł wybrać mały zestaw roboczy bardzo szybko. Jeśli się zgadzasz, dodaj to, a ja to zaakceptuję.
jeremcc
2
W moim poprzednim komentarzu mam na myśli to, że stwierdzenie: „Kiedy indeksujesz pole bitowe (lub jakiś wąski zakres), zmniejszasz zbiór roboczy tylko o połowę” nie jest prawdą, jeśli rozkład jest silnie wyważony w kierunku jednej wartości. Ale podoba mi się reszta twojej odpowiedzi, więc jeśli to naprawisz, zaakceptuję ją.
jeremcc,
1
Gotowe. Myślałem, że dla miliona wierszy pole bitowe będzie miało 50% rozkład, ale masz rację, że w przypadku konkretnego obszaru problemowego może to znacznie zmniejszyć zestaw roboczy.
Geoff Cox,
Warto przyjrzeć się planom wykonania z indeksem i bez niego i sprawdzić, czy indeks jest używany i czy rzeczywiście zmniejsza koszt zapytań. Łatwe i naukowe!
onupdatecascade
A co z indeksowaniem pola bitowego + innego pola? Na przykład. w dzienniku aktywności sieciowej można by zaindeksować sygnaturę czasową, ale inny przydatny indeks może znajdować się w polu bitowym „IsHTTPS” + sygnatura czasowa, aby szybko wyświetlić wszystkie akcje https. Czy to również byłoby nieefektywne?
składnik_15939
19

Właśnie natknąłem się na to pytanie z innej strony. Zakładając, że twoje stwierdzenie, że tylko kilka rekordów przyjmuje wartość 1 (i są to te, które Cię interesują), filtrowany indeks może być dobrym wyborem. Coś jak:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Spowoduje to utworzenie znacznie mniejszego indeksu, którego optymalizator jest wystarczająco inteligentny, aby użyć go, gdy jest to predykat w zapytaniu.

Ben Thul
źródło
1
Warto zauważyć, że predykat w zapytaniu musi być zakodowany na stałe do wartości w filtrowanym indeksie. Jeśli przekażesz wartość w parametrze yourBitColumn = @value, optymalizator nie może określić, czy filtrowany indeks jest użyteczny.
geofftnz
2
Można to obejść, ale masz rację; optymalizator potrzebuje gwarancji w czasie kompilacji, że wartości dla wszelkich predykatów pasujących do predykatu indeksu filtrowanego są statyczne / niezmienne, ponieważ zadaniem optymalizatora jest utworzenie ogólnego planu, który będzie działał dla dowolnego zestawu parametrów.
Ben Thul
9

100 milionów rekordów, a tylko kilka ma pole bitowe ustawione na 1? Tak, myślę, że indeksowanie pola bitowego zdecydowanie przyspieszyłoby odpytywanie rekordów bit = 1. Powinieneś pobrać logarytmiczny czas wyszukiwania z indeksu, a następnie dotknąć tylko kilku stron z rekordami bit = 1. W przeciwnym razie musiałbyś dotknąć wszystkich stron tabeli rekordów 100 milionów.

Z drugiej strony, zdecydowanie nie jestem ekspertem od baz danych i może brakować czegoś ważnego.

KRAKÓW 76
źródło
8

Jeśli twoja dystrybucja jest dość znana i niezrównoważona, na przykład 99% wierszy ma bit = 1, a 1% to bit = 0, kiedy wykonasz klauzulę WHERE z bitem = 1, pełne skanowanie tabeli będzie mniej więcej w tym samym czasie co skanowanie indeksu. Jeśli chcesz mieć szybkie zapytanie, w którym bit = 0, najlepszym sposobem, jaki znam, jest utworzenie przefiltrowanego indeksu, dodając klauzulę WHERE bit = 0. W ten sposób indeks ten będzie przechowywać tylko 1% wiersz. Wtedy wykonanie WHERE bit = 0 po prostu pozwoli optymalizatorowi zapytań wybrać ten indeks, a wszystkie wiersze z niego będą miały bit = 0. Masz również tę zaletę, że wymagana jest bardzo mała ilość miejsca na dysku, porównując pełny indeks na tym bicie .

Philippe Boucher
źródło
2
Jeśli 99% wierszy ma bit = 1, optymalizator powinien zignorować indeks i przeprowadzić skanowanie tabeli. Korzystanie z indeksu będzie faktycznie gorsze niż skanowanie tabeli, przynajmniej na dysku obrotowym, więcej operacji we / wy i niekolejnych odczytów z dysku. Filtrowany indeks (odpowiednik Postgresa: indeks częściowy) jest drogą do zrobienia. Wydaje mi się, że ponieważ minęło lata po zadaniu pytania, ta odpowiedź nie uzyskała głosów, na które zasługiwała.
Andrew Lazarus,
7

Chociaż nie sądzę, żebym indeksował TYLKO kolumnę bitową samodzielnie, bardzo często dołącza się kolumny bitowe jako część indeksu złożonego.

Prostym przykładem może być indeks ACTIVE, LASTNAME zamiast tylko nazwiska, gdy Twoja aplikacja prawie zawsze szuka aktywnych klientów.

BradC
źródło
7
W podanym przykładzie byłbym bardziej skłonny umieścić LastName na pierwszym miejscu. Zależy to od obciążenia określonego zapytania, ale generalnie posiadanie najpierw bardziej selektywnej kolumny oznacza, że ​​indeks jest bardziej prawdopodobny.
Mitch Wheat,
7

Jeśli tego nie czytałeś, Jason Massie napisał niedawno artykuł, w którym omówiono ten właśnie temat.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Edycja: nowa lokalizacja artykułu - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Maszyna Wayback dla poprzednio „Nowego” artykułu: http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

Nowa lokalizacja SQL Server Pedia to Toadworld, w której znajduje się nowy artykuł od Kennetha Fishera omawiający ten temat:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be- used.aspx

maszyna wayback: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an -index-on-a-bit-column-will-never-be-used.aspx

Jeff
źródło
ten artykuł nie jest już widoczny
Homer6
@ Homer6 Dodałem link do tego, jak wygląda nowy dom dla tego artykułu.
Jeff,
Nowy link prowadzi do strony głównej Toad World.
Zachód
Znaleziono artykuł przy użyciu maszyny Wayback i znalazłem nowy powiązany artykuł. Mam nadzieję że to pomoże.
Jeff
2

Oczywiście, że warto, zwłaszcza jeśli chcesz pobrać dane według tej wartości. Byłoby to podobne do użycia rzadkiej macierzy zamiast normalnej macierzy.

Teraz w SQL 2008 możesz korzystać z funkcji partycjonowania i możesz filtrować dane, które trafiają do indeksu. Wadą wcześniejszych wersji byłoby to, że indeks byłby tworzony dla wszystkich danych, ale można to zoptymalizować, przechowując interesujące wartości w oddzielnej grupie plików.

Bogdan Maxim
źródło
2

Jak powiedzieli inni, będziesz chciał to zmierzyć. Nie pamiętam, gdzie to przeczytałem, ale kolumna musi mieć bardzo wysoką liczność (około 95%), aby indeks był skuteczny. Najlepszym sposobem na to byłoby zbudowanie indeksu i zbadanie planów wykonania dla wartości 0 i 1 pola BIT. Jeśli widzisz operację wyszukiwania indeksu w planie wykonania, wiesz, że Twój indeks będzie używany.

Najlepszym sposobem postępowania byłoby przetestowanie podstawowej tabeli SELECT * FROM WHERE BitField = 1; zapytaj i powoli buduj funkcjonalność z tego miejsca krok po kroku, aż uzyskasz realistyczne zapytanie dla swojej aplikacji, sprawdzając plan wykonania na każdym kroku, aby upewnić się, że wyszukiwanie indeksu jest nadal używane. Wprawdzie nie ma gwarancji, że ten plan wykonania zostanie wykorzystany w produkcji, ale jest duża szansa, że ​​tak będzie.

Niektóre informacje można znaleźć na forach sql-server-performance.com oraz w przywołanym artykule

Jeremiah Peschka
źródło
Nie tak bardzo liczy się liczność kolumny jako całości. Jest to selektywność klauzuli WHERE. Więc jeśli jest kilka kolumn o wartości 1, indeksowanie może być nadal dobre. Jeśli jest to 50/50 (np. Mężczyzna / kobieta), to nie jest tego warte.
WW.
2

„Pamiętam, jak kiedyś przeczytałem, że indeksowanie pola o niskiej liczności (mała liczba odrębnych wartości) nie jest naprawdę warte wykonywania”

Dzieje się tak, ponieważ SQL Server prawie zawsze okaże się bardziej efektywne, aby po prostu wykonać skanowanie tabeli niż odczytać indeks. Więc w zasadzie twój indeks nigdy nie zostanie użyty i szkoda go utrzymywać. Jak powiedzieli inni, może to być w porządku w indeksie złożonym.

DJ.
źródło
2

Jeśli Twoim celem jest szybsze wyszukiwanie rekordów, w których wartość pola bitowego jest równa „1”, możesz wypróbować zindeksowany widok tabeli bazowej, która zawiera tylko rekordy, w których pole bitowe jest równe „1”. W wersji korporacyjnej, jeśli zapytanie może korzystać z widoku indeksowanego zamiast określonej tabeli w celu poprawy wydajności zapytania, użyje widoku. Teoretycznie zwiększyłoby to szybkość zapytań wybierających, które szukają tylko rekordów z wartością pola bitowego równą „1”.

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Wszystko to zakłada, że ​​jesteś Microsoft SQL Server 2005 Enterprise. To samo może dotyczyć roku 2008, nie znam tej wersji.


źródło
2

Jeśli chcesz wiedzieć, czy indeks ma pożądane efekty: przetestuj i przetestuj ponownie.

Ogólnie rzecz biorąc, nie potrzebujesz indeksu, który nie zawęża wystarczająco tabeli, ze względu na koszt utrzymania indeksu. (koszt> zysk). Ale jeśli indeks w twoim przypadku przeciął stół na pół, możesz coś zyskać, ale nie wyrzucisz tego na stół. Wszystko zależy od dokładnego rozmiaru / struktury tabeli i tego, jak z niej korzystasz (liczba odczytów / zapisów).

thijs
źródło
1

Samo w sobie nie, ponieważ powoduje to bardzo małą selektywność. Jako część indeksu złożonego. całkiem możliwe, ale tylko po innych kolumnach równości.

Craig Nicholson
źródło
1

Nie można indeksować pola bitowego w programie SQL Server 2000, jak wskazano wówczas w Books Online:

kawałek

Całkowity typ danych 1, 0 lub NULL.

Uwagi

Kolumny typu bit nie mogą mieć indeksów.

Tak, jeśli masz tylko kilka wierszy spośród milionów, indeks pomoże. Ale jeśli chcesz to zrobić w tym przypadku, musisz zrobić kolumnę a tinyint.

Uwaga : Enterprise Manager nie pozwoli ci utworzyć indeksu na kolumnie bitowej. Jeśli chcesz, możesz nadal ręcznie utworzyć indeks na kolumnie bitowej:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Ale SQL Server 2000 tak naprawdę nie będzie korzystał z takiego indeksu - uruchamiając zapytanie, w którym indeks byłby idealnym kandydatem, np .:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 wykona zamiast tego skanowanie tabeli, zachowując się tak, jakby indeks w ogóle nie istniał. Jeśli zmienisz kolumnę na tinyint, SQL Server 2000 wykona przeszukiwanie indeksu. Ponadto następujące nieobjęte zapytanie:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Wykona przeszukiwanie indeksu, po którym nastąpi wyszukiwanie zakładek.


SQL Server 2005 ma ograniczoną obsługę indeksów w kolumnach bitowych. Na przykład:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

spowoduje przeszukanie indeksu przez indeks pokrywający. Ale przypadek nieobjęty:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

nie spowoduje przeszukania indeksu, po którym nastąpi wyszukiwanie zakładek, wykona skanowanie tabeli (lub skanowanie indeksu klastrowego) zamiast przeszukiwania indeksu, po którym nastąpi wyszukiwanie zakładek.

Weryfikowane przez eksperymenty i bezpośrednią obserwację.

Ian Boyd
źródło
FYI - SQL Server 2005 Management Studio pozwala ci to zrobić.
jeremcc,
Moja kopia SQL Server 2000 pozwoliła mi ustawić indeks w kolumnie bitowej.
Kibbee
Moja kopia programu SQL Server 2000 nie pozwala mi ustawić indeksu w kolumnie bitowej.
Ian Boyd
1

bardzo późna odpowiedź ...

Tak, może być przydatne według zespołu SQL CAT (zaktualizowane, skonsolidowane)

gbn
źródło
1
Wydaje się, że łącze jest teraz martwe. Wydaje się jednak, że ten post został skonsolidowany wraz z kilkoma innymi w e-booku . Przywoływana sekcja zaczyna się na stronie 86. E-book można pobrać z eBooków SQLCAT.com pod łączem „Przewodnik po silniku relacyjnym SQLCAT”.
mwolfe02
0

Czy to typowe zapytanie? Może się to opłacać, szukając „garści” płyt, ale nie pomoże ci zbytnio w innych wierszach. Czy istnieją inne sposoby identyfikacji danych?

jason saldo
źródło
0

Kardynalność to jeden czynnik, a drugi to to, jak dobrze indeks dzieli dane. Jeśli masz około połowy jedynek i połowę zer, to pomoże. (Zakładając, że ten indeks jest lepszą ścieżką do wyboru niż jakiś inny indeks). Jednak jak często wstawiasz i aktualizujesz? Dodanie indeksów dla wydajności SELECT również szkodzi wydajności INSERT, UPDATE i DELETE, więc miej to na uwadze.

Powiedziałbym, że jeśli 1 do 0 (lub odwrotnie) nie jest lepsze niż 75% do 25%, nie przejmuj się.

Anthony Potts
źródło
1
Nie zgodziłbym się. Jeśli twoja dystrybucja to 50/50, nigdy nie użyjesz indeksu, ponieważ szybsze byłoby wykonanie skanowania tabeli. Jeśli jednak masz tylko 5, 1 wartości i 1 milion 0 wartości, najprawdopodobniej użyjesz indeksu podczas wyszukiwania 1.
Kibbee,
0

mierzyć czas reakcji przed i po i przekonać się, czy warto; teoretycznie powinno to poprawić wydajność zapytań używających indeksowanych pól, ale tak naprawdę zależy to od dystrybucji wartości prawda / fałsz i innych pól zaangażowanych w zapytania, które Cię interesują

Steven A. Lowe
źródło
0

Ian Boyd ma rację, mówiąc, że nie można tego zrobić za pomocą Enterprise Manager dla SQL 2000 (patrz jego uwaga dotycząca tworzenia go za pomocą T-SQL.

John B.
źródło
0

Musisz być sprytny, aby zapytać, musisz znać wartość obciążenia swojej kolumny, jeśli w systemie jest więcej obciążenia z prawdą i chcesz sprawdzić wszystkie wartości prawdziwe, napisz zapytanie, aby sprawdzić, czy nie jest fałszywe .. to bardzo pomoże , to tylko sztuczka.

Chetan Verma
źródło