Czy istnieje wymierna różnica w wydajności między używaniem INT i VARCHAR jako klucza podstawowego w MySQL? Chciałbym użyć VARCHAR jako klucza podstawowego dla list referencyjnych (pomyśl o stanach USA, kodach krajów), a współpracownik nie ustąpi na INT AUTO_INCREMENT jako klucz podstawowy dla wszystkich tabel.
Mój argument, tak szczegółowo tutaj , jest taki, że różnica w wydajności między INT i VARCHAR jest pomijalna, ponieważ każde odwołanie do klucza obcego INT będzie wymagało JOIN, aby nadać sens odwołaniu, klucz VARCHAR bezpośrednio przedstawia informacje.
Czy ktoś ma więc doświadczenie z tym konkretnym przypadkiem użycia i związanymi z nim problemami z wydajnością?
mysql
performance
primary-key
innodb
myisam
Jake McGraw
źródło
źródło
Odpowiedzi:
Warto zauważyć, że można uniknąć pewnej liczby połączonych zapytań, używając tak zwanego klucza naturalnego zamiast klucza zastępczego . Tylko Ty możesz ocenić, czy korzyści z tego są znaczące w Twoim wniosku.
Oznacza to, że można mierzyć zapytania w aplikacji, które są najważniejsze dla szybkości, ponieważ działają z dużymi ilościami danych lub są wykonywane bardzo często. Jeśli te zapytania skorzystają na wyeliminowaniu sprzężenia i nie cierpią z powodu użycia klucza podstawowego varchar, zrób to.
Nie używaj żadnej strategii dla wszystkich tabel w bazie danych. Jest prawdopodobne, że w niektórych przypadkach klucz naturalny jest lepszy, ale w innych przypadkach klucz zastępczy jest lepszy.
Inni zauważają, że w praktyce rzadko zdarza się, aby naturalny klucz nigdy się nie zmieniał lub nie miał duplikatów, więc klucze zastępcze są zwykle opłacalne.
źródło
Nie chodzi o wydajność. Chodzi o to, co stanowi dobry klucz podstawowy. Unikalne i niezmienne w czasie. Możesz pomyśleć, że jednostka taka jak kod kraju nigdy nie zmienia się w czasie i byłaby dobrym kandydatem na klucz podstawowy. Ale gorzkie doświadczenie jest takie rzadkie.
INT AUTO_INCREMENT spełnia warunek „unikalny i niezmienny w czasie”. Stąd preferencja.
źródło
Byłem trochę zirytowany brakiem benchmarków dla tego online, więc sam przeprowadziłem test.
Zwróć jednak uwagę, że nie robię tego regularnie, więc sprawdź moją konfigurację i kroki pod kątem jakichkolwiek czynników, które mogły mieć wpływ na wyniki w niezamierzony sposób, i opublikuj swoje obawy w komentarzach.
Konfiguracja wyglądała następująco:
Stoły:
Następnie wypełniłem 10 milionów wierszy w każdej tabeli skryptem PHP, którego istota jest następująca:
W przypadku
int
tabel bit($keys[rand(0, 9)])
został zastąpiony justemrand(0, 9)
, a dlavarchar
tabel użyłem pełnych nazw stanów w Stanach Zjednoczonych, bez cięcia lub rozszerzania ich do 6 znaków.generate_random_string()
generuje 10-znakowy losowy ciąg.Następnie uruchomiłem w MySQL:
SET SESSION query_cache_type=0;
jan_int
stołu:SELECT count(*) FROM jan_int WHERE myindex = 5;
SELECT BENCHMARK(1000000000, (SELECT count(*) FROM jan_int WHERE myindex = 5));
myindex = 'califo'
dlachar
tabel imyindex = 'california'
dlavarchar
tabel.Czasy
BENCHMARK
zapytania w każdej tabeli:Jeśli chodzi o rozmiary tabel i indeksów, oto wynik
show table status from janperformancetest;
(bez kilku kolumn):Mój wniosek jest taki, że nie ma różnicy w wydajności w tym konkretnym przypadku użycia.
źródło
INDEX
zamiastPRIMARY KEY
. Nie pamiętam swojego rozumowania - prawdopodobnie założyłem, żePRIMARY KEY
jest to tylkoINDEX
ograniczenie z wyjątkowością. Jednak czytając sekcję o tym, jak rzeczy są przechowywane w InnoDB na federico-razzoli.com/primary-key-in-innodb , myślę, że moje wyniki nadal mają zastosowanie do kluczy głównych i odpowiadam na pytanie dotyczące różnicy w wydajności wyszukiwania wartości. Twój komentarz sugeruje również przyjrzenie się wydajności algorytmów sortowania , które nie mają zastosowania do przypadku użycia, który badam, czyli wyszukiwania wartości w zestawie.Zależy od długości. Jeśli varchar będzie miał 20 znaków, a int to 4, to jeśli użyjesz int, twój indeks będzie miał PIĘĆ razy więcej węzłów na stronę miejsca indeksowego na dysku ... Oznacza to, że przechodzenie indeks będzie wymagał jednej piątej odczytów fizycznych i / lub logicznych.
Tak więc, jeśli wydajność jest problemem, biorąc pod uwagę możliwość, zawsze używaj integralnego, nieistotnego klucza (zwanego surogatem) dla swoich tabel, a dla kluczy obcych, które odwołują się do wierszy w tych tabelach ...
Jednocześnie , aby zagwarantować spójność danych, każda tabela, w której ma to znaczenie, powinna również mieć znaczący nienumeryczną klucz alternatywny (lub unikalny indeks), aby upewnić się, że zduplikowane wiersze nie mogą być wstawiane (duplikat na podstawie znaczących atrybutów tabeli).
W przypadku konkretnego zastosowania, o którym mówisz (np. Sprawdzania stanu), nie ma to znaczenia, ponieważ rozmiar tabeli jest tak mały. Ogólnie nie ma wpływu na wydajność indeksów w tabelach zawierających mniej niż kilka tysięcy wierszy. ..
źródło
Absolutnie nie.
Wykonałem kilka ... kilka ... testów wydajności między INT, VARCHAR i CHAR.
Tabela 10 milionów rekordów z KLUCZEM PODSTAWOWYM (unikalnym i zgrupowanym) miała dokładnie taką samą szybkość i wydajność (oraz koszt poddrzewa) bez względu na to, którego z trzech użyłem.
Biorąc to pod uwagę ... używaj tego, co jest najlepsze dla Twojej aplikacji. Nie martw się o wydajność.
źródło
W przypadku krótkich kodów prawdopodobnie nie ma różnicy. Jest to szczególnie prawdziwe, ponieważ tabela zawierająca te kody jest prawdopodobnie bardzo mała (maksymalnie kilka tysięcy wierszy) i nie zmienia się często (kiedy ostatnio dodawaliśmy nowy stan USA).
W przypadku większych stołów z szerszą odmianą klucza może to być niebezpieczne. Pomyśl na przykład o użyciu adresu e-mail / nazwy użytkownika z tabeli User. Co się dzieje, gdy masz kilka milionów użytkowników, a niektórzy z nich mają długie nazwy lub adresy e-mail. Teraz za każdym razem, gdy musisz dołączyć do tego stołu za pomocą tego klucza, staje się to znacznie droższe.
źródło
Jeśli chodzi o klucz podstawowy, to wszystko, co fizycznie czyni wiersz unikalnym, powinno być określone jako klucz podstawowy.
W przypadku odniesienia jako klucza obcego użycie automatycznie zwiększającej się liczby całkowitej jako surogatu jest dobrym pomysłem z dwóch głównych powodów.
- Po pierwsze, łączenie jest zwykle mniej kosztowne.
- Po drugie, jeśli chcesz zaktualizować tabelę zawierającą unikalny varchar, aktualizacja musi spaść kaskadowo do wszystkich tabel podrzędnych i zaktualizować je wszystkie, a także indeksy, podczas gdy w przypadku surogatu int musi tylko zaktualizować tabela główna i jej indeksy.
Wyciąg z używania surogatu polega na tym, że prawdopodobnie możesz pozwolić na zmianę znaczenia surogatu:
Wszystko zależy od tego, o co naprawdę musisz się martwić w swojej strukturze i co oznacza najbardziej.
źródło
Typowe przypadki, w których surogat
AUTO_INCREMENT
boli:Typowym wzorcem schematu jest mapowanie wiele do wielu :
Wydajność tego wzorca jest znacznie lepsza, szczególnie w przypadku korzystania z InnoDB:
Czemu?
id
i jednego indeksu.Inny przypadek ( kraj ):
Zbyt często nowicjusz normalizuje kod_krajowy do 4-bajtowego,
INT
zamiast używać „naturalnego” 2-bajtowego, prawie niezmiennego 2-bajtowego ciągu. Szybsze, mniejsze, mniej JOIN, bardziej czytelne.źródło
W HauteLook zmieniliśmy wiele naszych tabel, aby używały klawiszy naturalnych. Doświadczyliśmy rzeczywistego wzrostu wydajności. Jak wspomniałeś, wiele naszych zapytań używa teraz mniej sprzężeń, co sprawia, że zapytania są bardziej wydajne. Jeśli będzie to miało sens, użyjemy nawet złożonego klucza podstawowego. Mimo to niektóre tabele są po prostu łatwiejsze w obsłudze, jeśli mają klucz zastępczy.
Ponadto, jeśli pozwalasz ludziom pisać interfejsy do Twojej bazy danych, pomocny może być klucz zastępczy. Strona trzecia może polegać na fakcie, że klucz zastępczy zmieni się tylko w bardzo rzadkich okolicznościach.
źródło
Stałem przed tym samym dylematem. Zrobiłem DW (schemat konstelacji) z 3 tabelami faktów, Wypadki drogowe, Pojazdy w wypadkach i Ofiary w wypadkach. Dane obejmują wszystkie wypadki zarejestrowane w Wielkiej Brytanii w latach 1979–2012 oraz 60 tabel wymiarów. Łącznie około 20 milionów rekordów.
Relacje między tabelami faktów:
RDMS: MySQL 5.6
Natywnie indeks wypadków to varchar (cyfry i litery), składający się z 15 cyfr. Starałem się nie mieć kluczy zastępczych, gdy indeksy wypadków nigdy się nie zmienią. W komputerze i7 (8 rdzeni) DW stał się zbyt wolny, aby przeszukać 12 milionów rekordów obciążenia w zależności od wymiarów. Po wielu przeróbkach i dodaniu zastępczych kluczy biginta uzyskałem średni wzrost wydajności o 20%. Jeszcze do niskiego wzrostu wydajności, ale ważna próba. Pracuję nad strojeniem i klastrowaniem MySQL.
źródło
Pytanie dotyczy MySQL, więc mówię, że jest znacząca różnica. Jeśli chodziło o Oracle (która przechowuje liczby jako ciąg - tak, na początku nie mogłem w to uwierzyć), to nie ma dużej różnicy.
Przechowywanie w tabeli nie jest problemem, ale aktualizowanie i odwoływanie się do indeksu. Zapytania wymagające wyszukiwania rekordu na podstawie jego klucza podstawowego są częste - chcesz, aby pojawiały się tak szybko, jak to możliwe, ponieważ zdarzają się tak często.
Rzecz w tym, że procesor zajmuje się oczywiście 4-bajtowymi i 8-bajtowymi liczbami całkowitymi w krzemie . Porównywanie dwóch liczb całkowitych jest NAPRAWDĘ szybkie - dzieje się to w jednym lub dwóch cyklach zegara.
Spójrzmy teraz na ciąg - składa się on z wielu znaków (obecnie więcej niż jeden bajt na znak). Porównania dwóch ciągów w celu ustalenia pierwszeństwa nie można wykonać w jednym lub dwóch cyklach. Zamiast tego znaki łańcuchów muszą być iterowane, aż zostanie znaleziona różnica. Jestem pewien, że istnieją sztuczki, aby przyspieszyć to w niektórych bazach danych, ale to nie ma znaczenia tutaj, ponieważ porównanie int jest wykonywane naturalnie i błyskawicznie w krzemie przez procesor.
Moja ogólna zasada - każdy klucz podstawowy powinien być autoinkrementacją INT, szczególnie w aplikacjach OO korzystających z ORM (Hibernate, Datanucleus, cokolwiek), gdzie istnieje wiele relacji między obiektami - zwykle zawsze będą implementowane jako proste FK i możliwość DB, aby szybko rozwiązać te problemy, jest ważny dla szybkości reakcji aplikacji.
źródło
Nie mam pewności co do wpływu na wydajność, ale wydaje się, że możliwym kompromisem, przynajmniej w trakcie programowania, byłoby uwzględnienie zarówno automatycznie zwiększanego, całkowitego klucza zastępczego, jak i zamierzonego, unikalnego, „naturalnego” klucza. Dałoby to możliwość oceny wydajności, a także innych możliwych problemów, w tym zmienności kluczy naturalnych.
źródło
Jak zwykle nie ma ogólnych odpowiedzi. 'To zależy!' i nie żartuję. Moje rozumienie pierwotnego pytania dotyczyło kluczy na małych tabelach - takich jak Country (identyfikator całkowity lub kod char / varchar) będący kluczem obcym do potencjalnie ogromnej tabeli, takiej jak tabela adresów / kontaktów.
Istnieją dwa scenariusze, w których chcesz odzyskać dane z bazy danych. Pierwszy to rodzaj zapytania w postaci listy / wyszukiwania, w którym chcesz wyświetlić wszystkie kontakty z kodami stanów i krajów lub nazwami (identyfikatory nie pomogą i dlatego będą wymagały wyszukiwania). Drugi to scenariusz pobierania klucza podstawowego, który pokazuje pojedynczy rekord kontaktu, w którym należy podać nazwę stanu i kraju.
W przypadku tego ostatniego prawdopodobnie nie ma znaczenia, na czym oparty jest FK, ponieważ łączymy tabele dla pojedynczego rekordu lub kilku rekordów i odczytów kluczy. Nasz wybór może mieć wpływ na pierwszy scenariusz (wyszukiwanie lub lista). Ponieważ wymagane jest pokazanie kraju (przynajmniej rozpoznawalny kod i być może nawet samo wyszukiwanie zawiera kod kraju), potencjalnie może nie być konieczne dołączanie do innego stołu za pomocą klucza zastępczego (jestem tutaj ostrożny, ponieważ tak naprawdę nie testowałem to, ale wydaje się wysoce prawdopodobne) poprawić wydajność; niezależnie od tego, że z pewnością pomaga w poszukiwaniach.
Ponieważ kody mają niewielki rozmiar - zwykle nie więcej niż 3 znaki dla kraju i stanu, w tym scenariuszu można używać kluczy naturalnych jako kluczy obcych.
Drugi scenariusz, w którym klucze są zależne od dłuższych wartości varchar i być może od większych tabel; klucz zastępczy prawdopodobnie ma przewagę.
źródło
Pozwólcie, że powiem tak, jest zdecydowanie różnica, biorąc pod uwagę zakres wykonania (definicja poza pudełkiem):
1- Używanie surrogate int jest szybsze w aplikacji, ponieważ nie musisz używać ToUpper (), ToLower (), ToUpperInvarient () lub ToLowerInvarient () w swoim kodzie lub zapytaniu, a te 4 funkcje mają różne testy wydajności. Zobacz zasady wydajności firmy Microsoft na ten temat. (wykonanie aplikacji)
2- Użycie zastępczej int gwarantuje niezmienność klucza w czasie. Nawet kody krajów mogą się zmieniać, zobacz w Wikipedii, jak zmieniały się kody ISO w czasie. Zmiana klucza podstawowego dla poddrzew zajęłaby dużo czasu. (wykonanie utrzymania danych)
3- Wydaje się, że są problemy z rozwiązaniami ORM, takimi jak NHibernate, gdy PK / FK nie jest int. (wydajność programisty)
źródło