Prowadzę ciągłą debatę z różnymi programistami w moim biurze na temat kosztu indeksu i tego, czy unikalność jest korzystna czy kosztowna (prawdopodobnie obie). Sednem problemu są nasze konkurujące zasoby.
tło
Wcześniej czytałem dyskusję, w której stwierdzono, że Unique
indeks nie jest dodatkowym kosztem do utrzymania, ponieważ Insert
operacja domyślnie sprawdza, gdzie mieści się w drzewie B, a jeśli duplikat zostanie znaleziony w nieunikalnym indeksie, dołącza unikalizator do koniec klucza, ale w przeciwnym razie wstawia bezpośrednio. W tej sekwencji zdarzeń Unique
indeks nie ma dodatkowych kosztów.
Mój współpracownik zwalcza to stwierdzenie, mówiąc, że Unique
jest egzekwowane jako druga operacja po poszukiwaniu nowej pozycji w drzewie B, a zatem jego utrzymanie jest bardziej kosztowne niż indeks nieunikalny.
W najgorszym przypadku widziałem tabele z kolumną tożsamości (z natury unikatową), która jest kluczem do klastrowania tabeli, ale jest wyraźnie określona jako nie-unikalna. Z drugiej strony najgorsza jest moja obsesja na punkcie wyjątkowości, a wszystkie indeksy są tworzone jako unikalne, a gdy nie jest możliwe zdefiniowanie wyraźnie unikalnej relacji do indeksu, dołączam PK tabeli na końcu indeksu, aby zapewnić wyjątkowość jest gwarantowana.
Często biorę udział w recenzowaniu kodu dla zespołu programistów i muszę być w stanie podać ogólne wytyczne dla nich. Tak, każdy indeks powinien zostać oceniony, ale jeśli masz pięć serwerów z tysiącami tabel i aż dwadzieścia indeksów w tabeli, musisz być w stanie zastosować kilka prostych reguł, aby zapewnić określony poziom jakości.
Pytanie
Czy wyjątkowość ma dodatkowy koszt zaplecza w Insert
porównaniu z kosztem utrzymania nieunikalnego indeksu? Po drugie, co jest złego w dodawaniu klucza podstawowego tabeli na końcu indeksu, aby zapewnić unikalność?
Przykładowa definicja tabeli
create table #test_index
(
id int not null identity(1, 1),
dt datetime not null default(current_timestamp),
val varchar(100) not null,
is_deleted bit not null default(0),
primary key nonclustered(id desc),
unique clustered(dt desc, id desc)
);
create index
[nonunique_nonclustered_example]
on #test_index
(is_deleted)
include
(val);
create unique index
[unique_nonclustered_example]
on #test_index
(is_deleted, dt desc, id desc)
include
(val);
Przykład
Przykład dodania Unique
klucza na końcu indeksu znajduje się w jednej z naszych tabel faktów. Jest Primary Key
to jest Identity
kolumna. Jednakże, Clustered Index
to zamiast kolumny schemat partycjonowania, a następnie trzech zagranicznych kluczowych wymiarów bez wyjątkowości. Wybrana wydajność na tym stole jest fatalna i często uzyskuję lepsze czasy wyszukiwania, korzystając Primary Key
z wyszukiwania kluczowego, zamiast korzystać z niego Clustered Index
. Inne tabele, które mają podobny projekt, ale mają Primary Key
dołączone na końcu, mają znacznie lepszą wydajność.
-- date_int is equivalent to convert(int, convert(varchar, current_timestamp, 112))
if not exists(select * from sys.partition_functions where [name] = N'pf_date_int')
create partition function
pf_date_int (int)
as range right for values
(19000101, 20180101, 20180401, 20180701, 20181001, 20190101, 20190401, 20190701);
go
if not exists(select * from sys.partition_schemes where [name] = N'ps_date_int')
create partition scheme
ps_date_int
as partition
pf_date_int all
to
([PRIMARY]);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.bad_fact_table'))
create table dbo.bad_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
fk_id int not null,
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_bad_fact_table] clustered (date_int, group_id, group_entity_id, fk_id)
)
on ps_date_int(date_int);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.better_fact_table'))
create table dbo.better_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_better_fact_table] clustered(date_int, group_id, group_entity_id, id)
)
on ps_date_int(date_int);
go
Case
iIf
struktury są ograniczone do 10 poziomów, sensowne jest, że istnieje również limit rozwiązywania nieunikalnych bytów. W twoim oświadczeniu brzmi to tak, jakby dotyczyło tylko przypadków, gdy klucz klastrowania nie jest unikalny. Czy jest to problem dlaNonclustered Index
czy klucz klastrowania jest,Unique
czy nie ma problemu zNonclustered
indeksami?Nie zamierzam zastanawiać się nad tym, czy indeks powinien być unikalny, czy też nie, i czy w tym podejściu jest więcej kosztów ogólnych. Ale kilka rzeczy przeszkadzało mi w twoim ogólnym projekcie
WHERE is_deleted = 0
) i skorzystaj z filtrowanego indeksu. Rozważałbym nawet użycie 2 filtrowanych indeksów, jednego dlawhere is_deleted = 0
drugiego, a drugiego dlawhere is_deleted = 1
Zasadniczo wygląda to bardziej na ćwiczenie kodowania zaprojektowane do testowania hipotezy, a nie prawdziwy problem / rozwiązanie, ale te dwa wzorce są zdecydowanie czymś, czego szukam w recenzjach kodu.
źródło
Nonclustered
indeksie klucz klastrowania zostanie dołączony na końcu wiersza danych w celu wyszukiwania kluczy wewnętrznie. Jako takie, dwa indeksy są fizycznie takie same, co było celem mojego pytania.Wygląda na to, że po prostu używasz PK, aby utworzyć alternatywny, mniejszy indeks. Dlatego wydajność na nim jest szybsza.
Widać to w firmach, które mają ogromne tabele danych (np .: tabele danych podstawowych). Ktoś decyduje się na jeden ogromny indeks klastrowany, oczekując, że spełni on potrzeby różnych grup sprawozdawczych.
Ale jedna grupa może potrzebować tylko kilku części tego indeksu, podczas gdy inna grupa potrzebuje innych części ... więc indeks uderzający w każdą kolumnę pod słońcem w celu „zoptymalizowania wydajności” tak naprawdę nie pomaga.
Tymczasem rozbicie go w celu utworzenia wielu mniejszych ukierunkowanych wskaźników często rozwiązuje problem.
I wydaje się, że to właśnie robisz. Masz ten masywny indeks klastrowy o okropnej wydajności, a następnie używasz PK do utworzenia kolejnego indeksu z mniejszą liczbą kolumn, które (co nie dziwi) ma lepszą wydajność.
Więc po prostu przeanalizuj i dowiedz się, czy możesz wziąć pojedynczy indeks klastrowany i podzielić go na mniejsze, ukierunkowane indeksy, których potrzebują określone zadania.
Trzeba będzie wtedy przeanalizować wydajność z punktu widzenia „pojedynczego indeksu vs. wielu indeksów”, ponieważ tworzenie i aktualizacja indeksów wiąże się z dodatkowymi kosztami. Ale musisz to przeanalizować z ogólnej perspektywy.
EG: może być mniej zasobochłonny w stosunku do jednego ogromnego indeksu klastrowego, a bardziej zasobochłonny, aby mieć kilka mniejszych indeksów ukierunkowanych. Jeśli jednak będziesz w stanie szybciej uruchamiać ukierunkowane zapytania na zapleczu, oszczędzając czas (i pieniądze), być może warto.
Musisz więc przeprowadzić kompleksową analizę ... nie tylko spojrzeć na to, jak wpływa na twój świat, ale także na użytkowników końcowych.
Po prostu czuję, że źle używasz identyfikatora PK. Ale możesz używać systemu baz danych, który pozwala tylko na 1 indeks (?), Ale możesz się zakraść, jeśli masz PK (b / c każdy system relacyjnych baz danych w tych dniach wydaje się automatycznie indeksować PK). Jednak większość nowoczesnych RDBMS powinna umożliwiać tworzenie wielu indeksów; liczba indeksów, które możesz wprowadzić, nie powinna być ograniczona (w przeciwieństwie do limitu 1 PK).
Tak więc, tworząc PK, który działa jak indeks altowy, zużywasz PK, co może być potrzebne, jeśli później tabela zostanie rozszerzona.
Nie oznacza to, że twój stół nie potrzebuje PK. SOP DB 101 mówi „każdy stół powinien mieć PK”. Ale w sytuacji hurtowni danych itp. Posiadanie PK na stole może być dodatkowym narzutem, którego nie potrzebujesz. Lub może to być wysłanie boga, aby upewnić się, że nie dodajesz podwójnie wpisów duplikatów. To naprawdę zależy od tego, co robisz i dlaczego to robisz.
Ale ogromne tabele zdecydowanie korzystają z indeksów. Ale założenie, że jeden masywny indeks klastrowy będzie najlepszy, to po prostu ... może być najlepszy ... ale zaleciłbym przetestowanie w środowisku testowym rozbijania indeksu na wiele mniejszych indeksów ukierunkowanych na konkretne scenariusze przypadków użycia.
źródło