Dlaczego używanie kluczy łańcuchowych jest ogólnie uważane za zły pomysł?

24

Martwi mnie to od dłuższego czasu. W większości przypadków, gdy chodzi o przechowywanie danych w strukturach takich jak tabele, programiści, książki i artykuły, nalega, aby indeksowanie elementów w tych strukturach według wartości ciągu było uważane za złą praktykę. Jednak jak dotąd nie znalazłem żadnego takiego źródła, które wyjaśniałoby również DLACZEGO uważa się to za złą praktykę. Czy to zależy od języka programowania? Na podstawowych zasadach? W sprawie wdrożenia?

Weź dwa proste przykłady, jeśli to pomoże:

Tabela podobna do SQL, w której wiersze są indeksowane kluczem podstawowym String.

Słownik .NET, w którym kluczami są ciągi znaków.


źródło
9
Posiadanie kluczy ciągów nie jest złym pomysłem. Podejrzewam, że te oświadczenia zostały wydane w kontekście, w którym dostępny jest lepszy typ klucza. Przez cały czas mam słowniki .net z kluczami łańcuchowymi. Czy możesz podać kilka przykładów tego roszczenia?
CodesInChaos
3
Zwykle potrzebujesz kluczy podstawowych, które nie zmieniają się przez cały okres istnienia obiektu / wiersza. Na przykład, usernameponieważ klucz podstawowy userstabeli prawdopodobnie nie jest najlepszym pomysłem i wolisz identyfikator automatycznego przyrostu. Ale ten usernameciąg jest przypadkowy, ponieważ
zmienność
W bazie danych zastanów się, w jaki sposób indeksowałby ciągi zamiast liczb całkowitych.
@CodesInChaos Chciałbym pamiętać, gdzie znalazłem większość przypadków, ale na razie mogę wkleić fragment, który przypomniał mi o problemie. To było z pokazu slajdów GDC autorstwa Valve, który omawiał dialogi gry i przechowywał fakty o świecie w parach <klucz = ciąg, wartość = obiekt>.
2
Ciągi są w porządku. Po prostu nie „magiczne” struny. Korzystając z tabeli skrótów, upewnij się, że w kodzie nie ma nagich ciągów. Należy unikać dużych wartości tekstowych jako kluczy, ponieważ nie działają one dobrze, ale w większości rzeczywistych sytuacji krótki ciąg tekstowy jest tak samo szybki jak liczba całkowita (nie są to ogromne bazy danych). Możesz także użyć alternatywnych kluczy, na przykład klucz podstawowy jest liczbą, ale jest też „ślimak” lub unikatowy ciąg znaków, który jest również unikalny.
ipaul

Odpowiedzi:

17

Wszystko to ma zasadniczo związek z dwiema rzeczami:

1) Szybkość wyszukiwania (na przykład liczby całkowite są znacznie lepsze)

2) Rozmiar indeksów (gdzie wybuchłyby indeksy łańcuchowe)

Teraz wszystko zależy od twoich potrzeb i wielkości zbioru danych. Jeśli tabela lub kolekcja zawiera 10-20 elementów, typ klucza nie ma znaczenia. Będzie bardzo szybki, nawet z kluczem ciągowym.

PS Może nie być związane z twoim pytaniem, ale Guids są również uważane za złe dla kluczy bazy danych (16 bajtów Guid vs. 4 bajty całkowite). W przypadku dużych ilości danych prowadnice spowalniają wyszukiwanie.

królik
źródło
Nie zawsze - możliwe są przyrostowe identyfikatory GUID. Indeksy będą nadal większe, ale kara za wyszukiwanie nie będzie już tak zła.
Sam
7
Właściwie są w porządku. Musisz spojrzeć na związek między czasem IO dysku czasowego a porównywaniem wartości w pamięci. Ponieważ czasy dostępu do dysku przytłaczają porównanie pamięci, jedyną rzeczą, która naprawdę ma znaczenie przy analizie wydajności bazy danych, jest IO. To, czy klucz jest identyfikatorem GUID, łańcuchem, czy liczbą całkowitą, nie jest tak naprawdę ważne. Rozmiar indeksu wpływa na to, ile wartości indeksu mieści się na jednej stronie, ale to, czy kluczem jest 4-bajtowy int (który może nie być wystarczająco duży i nie może zostać wygenerowany przez klienta), czy też wartość 16-bajtowa nie stanowi istotnego problemu. W niektórych bazach danych identyfikator wiersza może mieć rozmiar 16 bajtów.
ipaul
9

Jest jeszcze jeden problem z używaniem ciągów jako kluczy, a ściślej - z użyciem literałów ciągów jako kluczy, pomijając czystą wydajność / wydajność. Literówki. Jeśli użyjesz literałów łańcuchowych jako kluczy w słowniku, przygotujesz się na nieprzyjemną niespodziankę, gdy "ReceiverId"zostaniesz "RecieverId". Skonfiguruj stałe do przechowywania kluczowych wartości i użyj ich ponownie przy każdym dostępie do słownika.

Trywialne i oczywiste, można powiedzieć, ale oszałamiająca liczba przykładów kodu .NET w sieci używa literałów łańcuchowych, propagując tę ​​wątpliwą praktykę. Szczególnie winny jest program ASP.NET ze wszystkimi sesjami, ViewStates i QueryParams rozmieszczonymi w całej bazie kodu.

scrwtp
źródło
Nie trywialne IMHO. Widziałem także przypadki, w których są klucze "1"i "1 "w tej samej tabeli.
pswg
Staje się jeszcze bardziej zabawny, gdy dodajesz do miksu także rozróżnianie wielkości liter. Widziałem mnóstwo ludzi, w tym mnie, wpadających bezpośrednio w to.
Tony Hopkinson,
Nawet lepiej niż używanie stałych, przynajmniej w języku C #, zamiast tego używa się wyrażeń. W ten sposób możesz generować łańcuchy na podstawie nazw metod / właściwości itp., Aby Twoje wyszukiwania łańcuchów stały się bezpieczne dla typu i przyjazne dla refaktorów.
GoatInTheMachine
4

Jest tu wiele kompromisów. Właściwie często używam kluczy łańcuchowych, ale często dołączam zastępcze klucze zastępcze dla złączeń (oczywiście byłoby odwrotnie, gdybym używał MySQL). Są jednak przypadki, w których ja tego nie robię.

Po pierwsze jestem fanem deklarowania kluczy naturalnych jako klucza podstawowego, gdzie db może sobie z tym poradzić (na przykład PostgreSQL). Pomaga to w normalizacji i zapewnia bardziej przejrzysty projekt bazy danych. Klawisze zastępcze ułatwiają łączenie.

Są dwa powody, dla których zwykle dodam klucze zastępcze:

  1. Nie zawsze jest jasne, czym jest naturalny klucz. Czasami trzeba je zmienić. Zmiana naturalnego, złożonego klucza, gdy jest on używany do łączenia i integralności referencyjnej, jest skomplikowana i podatna na błędy.

  2. Łączenie wydajności na klawiszach kompozytowych jest problematyczne i kiedy pójdziesz naturalną trasą klucza, utkniesz tam.

Jednak w przypadkach, gdy klucz naturalny jest definicją, pojedynczą kolumną i tekstem, zwykle dołączam klucz ciągowy. Moim powodem jest to, że często unika się łączenia podczas wyszukiwania. Najczęstszym zastosowaniem jest zapewnienie odpowiedniego projektu db wokół przypadku użycia typów wyliczeniowych. W większości przypadków nie wymagają one dodatkowego łączenia w przypadku rutynowych zapytań. W takim przypadku klucze łańcuchowe jako klawisze łączenia mają więc sens.

Na przykład w LedgerSMB przechowujemy kategoryzacje kont. Są one identyfikowane przez odwołanie do ciągu. A niektóre inne dane są przechowywane z odwołaniem do ciągu, który służy do egzekwowania reguł dotyczących kombinacji kategoryzacji, które mogą mieć wpływ na konto. Logika jest potrzebna tylko przy zapisywaniu zestawu kategoryzacji, więc dołączamy do klucza ciąg.

Jeśli chodzi o to, dlaczego domyślnie byłyby to klucze całkowite, nie sądzę, że to tylko kwestia rozmiaru indeksu. Dużym problemem jest zarządzanie kluczami. Ponieważ klucz jest dowolny i możesz mieć do czynienia z milionami rekordów, musisz mieć sposób na generowanie unikatowych ciągów. Są przypadki, w których ludzie używają do tego UUID, ale istnieje niezerowa szansa na kolizję UUID, a gdzie przechowywane są miliardy rekordów, szansa ta staje się wystarczająco wysoka, którą można rzeczywiście zobaczyć, podczas gdy szansa na kolizję z przyrostowymi typami liczb całkowitych wynosi zero zgodnie z definicją.

Chris Travers
źródło
To nie jest niezerowe, jeśli uda się sprawić, że liczba całkowita zawinie się do zera. W przypadku niepodpisanego 32-bitowego typu, który znajduje się w odległości zaledwie 4G, co jest niepokojąco blisko z „miliardami rekordów”…
Donal Fellows
Jeśli masz bazę danych, którą możesz rozpoznać „błąd zamiast zawijać”, jest to zero. W każdym razie łatwiej jest zarządzać możliwością kolizji z rosnącymi liczbami całkowitymi niż z wartościami pseudolosowymi.
Chris Travers
1

Istnieje wiele potencjalnych problemów z używaniem ciągów jako kluczy, szczególnie jeśli chodzi o tabele podobne do sql. Jak wspomniano przez @bunny, indeksy dla twoich tabel będą większe, ale myślę, że co ważniejsze, wszelkie relacje kluczy obcych do tabeli będą obejmowały OBA tabele zawierające łańcuch w przeciwieństwie do identyfikatora lżejszej (całkowitej) . Jeśli okaże się, że istnieje jeszcze więcej tabel z odniesieniami do pierwszej, klucze ciągów zostaną rozpowszechnione w całej bazie danych.

Matthew Flynn
źródło
1

Sam w sobie nie jest złym pomysłem, zwykle z perspektywy 20/20 to zły kompromis projektowy. Elastyczność i zakres sznurka w porównaniu do dodatkowych kosztów i złożoności.

Jeśli liczba całkowita odpowiada zakresowi zadań, a większość kosztownego przetwarzania nie musi wiedzieć, co reprezentuje liczba całkowita, użyj jednego.

Tony Hopkinson
źródło
0

W jakiś sposób odzyskałeś nieprawidłowe dane z Hashtable.

Miałeś na myśli „DaytimeTelefon” lub „EveningTelefon”?

lub

Miałeś na myśli 1234567 lub 1234576?

Chociaż liczby są prawdopodobnie bardziej wydajne dla maszyny , za każdym razem, gdy coś pójdzie nie tak (i ​​tak się dzieje), to do ciebie i mnie należy zrozumienie, co się stało, i w tym momencie oszczędność kilku bajtów pamięci i kilka mikro (nano?) sekund za każdym razem traci wyrazistość .

Phill W.
źródło
1
W ten sposób otrzymujesz listę stałych, używając nazwy stałej w swoim kodzie do reprezentowania magicznej liczby ... Java wyłania się na ratunek, aby jeszcze bardziej ją wyodrębnić i pozostawiając ci tylko nazwę i porządek mapowanie niewidoczne.
jwenting
-1

Wiele kompromisów i nie ma jednej właściwej odpowiedzi. Wielu programistów nigdy nie rozważyłoby użycia kluczy łańcuchowych w bazie danych, ponieważ nie są świadomi haszowania i działania bazy danych. Klucze ciągów, o ile są albo wyjątkowo stabilne, albo pozbawione znaczenia (zastępcze), są dobrym wyborem w wielu przypadkach.

mech23
źródło
2
Ta odpowiedź nie dodaje niczego, co nie zostało powiedziane w innych odpowiedziach, które mówią lepiej.
Martijn Pieters
-2

klucz łańcucha ma sens, jeśli chodzi o tabelę wyszukiwania zawierającą około 10-100 krótkich rekordów łańcucha; powiązane dane są bardziej czytelne + np. śledzenie zmian (numeryczny / identyfikator GUID vs. ciąg np. „Administrator”); btw, baza danych członkostwa ASP.NET używa kluczy ciągów dla AspNetRoles.

alfred hitchcock
źródło