Kiedy klucz podstawowy należy zadeklarować jako nieklastrowany?

169

Tworząc testową bazę danych dla innego pytania, które zadałem wcześniej, przypomniałem sobie, że klucz podstawowy można zadeklarować NONCLUSTERED

Kiedy użyjesz NONCLUSTEREDklucza CLUSTEREDpodstawowego zamiast klucza podstawowego?

Z góry dziękuję

Stuart Blackler
źródło

Odpowiedzi:

187

Pytanie nie brzmi „kiedy PK powinien być NC”, ale zamiast tego należy zapytać „jaki jest właściwy klucz dla indeksu klastrowego”?

Odpowiedź naprawdę zależy od tego, w jaki sposób przeszukujesz dane . Indeks klastrowy ma przewagę nad wszystkimi innymi indeksami: ponieważ zawsze zawiera wszystkie kolumny, zawsze obejmuje. Dlatego zapytania, które mogą wykorzystać indeks klastrowany, z pewnością nie muszą wykorzystywać odnośników do spełnienia niektórych rzutowanych kolumn i / lub predykatów.

Kolejnym elementem układanki jest sposób użycia indeksu ? Istnieją trzy typowe wzory:

  • sondy, gdy w indeksie szukana jest pojedyncza wartość klucza
  • skanowanie zakresów, gdy pobierany jest zakres wartości kluczowych
  • sortuj według wymagań, gdy indeks może spełnić zamówienie, nie wymagając sortowania stop-and-go

Jeśli więc przeanalizujesz oczekiwane obciążenie (zapytania) i odkryjesz, że duża liczba zapytań użyłaby określonego indeksu, ponieważ używają określonego wzorca dostępu, który korzysta z indeksu, warto zaproponować ten indeks jako indeks klastrowany.

Jeszcze innym czynnikiem jest to, że klastrowany klucz indeksu jest kluczem wyszukiwania używanym przez wszystkie nieklastrowane indeksy, a zatem szeroki klastrowany klucz indeksu tworzy efekt falowania i poszerza wszystkie nieklastrowane indeksy, a szerokie indeksy oznaczają więcej stron, więcej operacji we / wy , więcej pamięci, mniej dobroci.

Dobry indeks klastrowany jest stabilny , nie zmienia się w czasie istnienia encji, ponieważ zmiana wartości klucza indeksu klastrowego oznacza, że ​​wiersz musi zostać usunięty i wstawiony z powrotem.

Dobry klastrowany indeks rośnie w kolejności nie losowej (każda nowo wstawiona wartość klucza jest większa niż poprzednia wartość), aby uniknąć podziału strony i fragmentacji (bez bałagania się przy pomocy FILLFACTORs).

Skoro już wiemy, co to jest dobry klastrowany klucz indeksu, czy klucz podstawowy (który jest logiczną właściwością modelowania danych) spełnia wymagania? Jeśli tak, to PK powinno być zgrupowane. Jeśli nie, PK powinien być nieklastrowany.

Aby podać przykład, rozważ tabelę faktów sprzedażowych. Każdy wpis ma identyfikator, który jest kluczem podstawowym. Jednak zdecydowana większość zapytań wymaga danych między datą a inną datą, dlatego najlepszym klastrowanym kluczem indeksu byłaby data sprzedaży , a nie identyfikator . Innym przykładem posiadania innego indeksu klastrowego niż klucz podstawowy jest bardzo niski klucz selektywności, taki jak „kategoria” lub „stan”, klucz o bardzo niewielu odrębnych wartościach. Posiadanie klastrowanego klucza indeksu z tym kluczem niskiej selektywności jako kluczem najbardziej na lewo, np. (state, id), Często ma sens ze względu na skany zakresów, które szukają wszystkich wpisów w określonym „stanie”.

Ostatnia uwaga na temat możliwości nieklastrowego klucza podstawowego nad stertą (tj. W ogóle nie ma indeksu klastrowanego). Może to być prawidłowy scenariusz, typowym powodem jest krytyczna wydajność wkładki luzem, ponieważ hałdy mają znacznie lepszą przepustowość wkładki luzem w porównaniu z indeksami klastrowymi.

Remus Rusanu
źródło
1
Co oznacza tutaj „sortuj według wymagań, gdy indeks może spełnić zamówienie, nie wymagając sortowania stop-and-go”?
Mike Sherrill „Cat Recall”
2
@RemusRusanu. +1 Bardzo przydatna odpowiedź. Jedno pytanie dotyczące przykładu (state, id). W tym przykładzie nie zostanie spełniony wymóg „dobry indeks klastrowy rosnący w przypadkowej kolejności”, prawda? Czy możemy zatem uznać to za dobry indeks klastrowy?
Lijo
26

Podstawowy powód korzystania z indeksów klastrowych jest podany na Wikipedii :

Klastrowanie zmienia blok danych w pewną odrębną kolejność w celu dopasowania do indeksu, w wyniku czego dane wiersza są przechowywane w kolejności. Dlatego w danej tabeli bazy danych można utworzyć tylko jeden indeks klastrowy. Indeksy klastrowe mogą znacznie zwiększyć ogólną szybkość wyszukiwania, ale zwykle tylko wtedy, gdy dane są uzyskiwane sekwencyjnie w tej samej lub odwrotnej kolejności indeksu klastrowego lub gdy wybrany jest zakres elementów.

Powiedz, że mam tabelę osób, a ci ludzie mają kolumnę Kraj i unikalny klucz podstawowy. To tabela demograficzna, więc to jedyne rzeczy, na których mi zależy; jaki kraj i ilu wyjątkowych ludzi jest związanych z tym krajem.

W związku z tym mogę tylko WYBRAĆ GDZIE LUB ZAMÓWIENIE WEDŁUG kolumny Kraj; indeks klastrowany na kluczu podstawowym nie robi mi nic dobrego, nie uzyskuję dostępu do tych danych przez PK, uzyskuję do nich dostęp za pośrednictwem tej drugiej kolumny. Ponieważ mogę mieć tylko jeden indeks klastrowany w tabeli, zadeklarowanie mojego PK jako Clustered uniemożliwiłoby mi użycie indeksu klastrowanego w kraju.

Ponadto, tutaj jest dobry artykuł na temat indeksów klastrowanych vs. indeksów nieklastrowanych, okazuje się, że indeksy klastrowe spowodowały problemy z wydajnością wstawiania w SQL Server 6.5 (co, mam nadzieję, nie jest istotne dla większości z nas tutaj).

Jeśli umieścisz indeks klastrowy w kolumnie TOŻSAMOŚCI, wówczas wszystkie wstawki pojawią się na ostatniej stronie tabeli - i ta strona będzie zablokowana na czas trwania każdej TOŻSAMOŚCI. Nic wielkiego ... chyba że masz 5000 osób, które chcą ostatniej strony. W takim razie masz spory o tę stronę

Pamiętaj, że nie dotyczy to późniejszych wersji.

Ben Brocka
źródło
3
FIY, wspomniałeś o SQL Server 6.5: dba.stackexchange.com/questions/1584/...
gbn 11.11.11
15

Jeśli klucz podstawowy należy do UNIQUEIDENTIFIER, należy go podać NONCLUSTERED. Jeśli utworzysz klaster, każda wstawka będzie musiała wykonać tasowanie rekordów, aby wstawić nowy wiersz we właściwej pozycji. Spowoduje to wydajność czołgu.

Bryan Johns
źródło
1
Chociaż staram się unikać identyfikatorów UUID dla kluczy klastrowych, uważam, że powyższe rozumowanie może być niepełne. Serwer SQL niekoniecznie przetasowuje wiersze, aby wstawić a we właściwej pozycji (jeśli masz na myśli „między wartością niższą a wyższą”). Zastanów się nad wstawką pośrodku tabeli bilionów rzędów. Dodatkowa pośrednia potrzeba, co może być tym, co miałeś na myśli. Istnieje UNIQUEIDENTIFIERrównież typ sekwencyjny , który ma takie samo prawdopodobieństwo wygenerowania unikalnych kluczy, choć nadal ma rozmiar 128.
Charles Burns,
7

Bardzo częsty przykład:

  • Customerstół z CustomerIDjakCLUSTERED PRIMARY KEY
  • Zamów tabelę z OrderID (PK), CustomerID, OrderDatei innymi kolumnami
  • OrderPositions z OrderPositionID (PK), OrderId, ProductID, Amount, Price ...
  • musisz zaindeksować tabele zamówień

Oczywiście „to zależy” jest - jak prawie zawsze - poprawną odpowiedzią, ale większość aplikacji (nie raportów BI) będzie działać w oparciu o klienta (np. Zalogujesz się jako klient 278 na stronie i klikniesz „Moje zamówienia” lub sprzedawca wymienia wszystkie zamówienia dla klienta 4569 lub twoja procedura fakturowania sumuje wszystkie zamówienia dla klienta 137).

W takim przypadku nie ma większego sensu grupowanie tabeli według OrderID. Tak, będziesz mieć pytania SELECT ... WHERE OrderId = ?dotyczące listy szczegółów zamówienia, ale zwykle będzie to krótki i tani (3 odczyty) indeks szuka.

Z drugiej strony, jeśli utworzysz klaster Orderwedług tabeli CustomerID, nie będzie musiał wykonywać wielu odnośników przy każdym zapytaniu o tabelę CustomerId = ?.

CLUSTERED INDEXPowinno być zawsze UNIQUE, w przeciwnym razie SQL Server będzie dodać niewidzialny (= bezużyteczny) kolumna INT UNIQUIFIERzapewnienie uniquiness - i to zrobić dużo więcej sensu, aby dodać Real (użytkowej) danych potem jakiś losowy (w zależności od kolejności wkładania) rzeczy.

Ponieważ klient (miejmy nadzieję) złoży więcej niż jedno zamówienie, musielibyśmy dodać albo OrderID(lub jeśli zazwyczaj to sortujesz) OrderDate(jeśli jest to data / godzina - w przeciwnym razie klient byłby ograniczony do jednego zamówienia dziennie) do CLUSTERED INDEXi kończy się z:

CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)

Te same zasady dotyczą OrderPositionstabeli. Zwykle większość zapytań zawiera listę wszystkich pozycji dla konkretnego zamówienia, więc powinieneś utworzyć PK z OrderPositionIDas NONCLUSTEREDi a UNIQUE CLUSTERED INDEXon OrderId, OrderPositionID.

BTW: poprawne jest, że Customertabela jest grupowana według jej PK ( CustomerIDponieważ jest to „Tabela najwyższego poziomu” i będzie - w typowej aplikacji - przeszukiwana przez jej identyfikator klienta.

Czyste jak np tablic przeglądowych Genderslub InvoiceTypesczy PaymentTypesą kolejnym przykładem tabel, które powinny być skupione przez jego PK (bo będziesz zwykle dołączyć je GenderId, InvoiceTypeIdlub PaymentTypeId).

Thomas Franz
źródło
2

Gdy indeks klastrowy jest uważany za bardziej korzystny dla całego systemu niż klastrowany PK przy użyciu pewnej miary wydajności. W tabeli może znajdować się tylko jeden indeks klastrowany.

Przykładowe miary wydajności to czas pojedynczego zapytania (szybkość), integracja całkowitych czasów zapytania z tabelą (wydajność) i konieczność dodania wielu kolumn zawierających do bardzo dużego indeksu nieklastrowego, aby osiągnąć wydajność podobną do klastrowanej (rozmiar ).

Może się to zdarzyć, gdy dane są zwykle pobierane przy użyciu indeksu, który nie jest unikalny, zawiera wartości null (niedozwolone w PK) lub PK został dodany z drugiego powodu (takiego jak replikacja lub identyfikacja rekordu ścieżki audytu).

Crokusek
źródło