Czy istnieje RZECZYWISTA różnica w wydajności między kluczami podstawowymi INT i VARCHAR?

174

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ą?

Jake McGraw
źródło
3
Napisałem post z odpowiedzią "nie", z kilkoma szczegółami testów, które przeprowadziłem ... ale to był SQL Server, a nie MySQL. Więc usunąłem odpowiedź.
Timothy Khouri
17
@Timothy - nie powinieneś go usuwać. Byłem w trakcie głosowania za tym. Większość serwerów baz danych SQL ma podobne planery zapytań i podobne wąskie gardła wydajności.
Paul Tomblin
9
@Timothy, opublikuj ponownie swoje wyniki.
Jake McGraw
2
Tak wiele komentarzy i odpowiedzi zakłada, że ​​klucze służą do łączenia. Oni nie są. Klucze służą do zapewnienia spójności danych - aby uniknąć zduplikowanych wierszy (więcej niż jeden wiersz reprezentujący tę samą jednostkę). Dowolna kolumna (lub zestaw kolumn) może być użyta w złączeniu, a aby zagwarantować, że sprzężenie jest typu jeden do zera lub wiele kolumn, po prostu muszą być unikalne. Każdy unikalny indeks to gwarantuje i nie musi mieć znaczenia.
Charles Bretana

Odpowiedzi:

78

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.

Bill Karwin
źródło
3
A czasami (imho, często), oba są lepsze, zastępstwo do użycia dla odniesień FK w innych tabelach i dla połączeń oraz naturalny klucz zapewniający spójność danych
Charles Bretana
@CharlesBretana To ciekawe. Czy używanie naturalnego klucza w celu zapewnienia spójności danych obok FK jest powszechną praktyką? Moją pierwszą myślą było to, że dodatkowa przestrzeń dyskowa, która byłaby wymagana przy dużych stołach, może nie być opłacalna. Każda informacja jest mile widziana. Do Twojej wiadomości - mam przyzwoite doświadczenie w programowaniu, ale moje doświadczenie w SQL ogranicza się głównie do zapytań SELECT
Rob
2
@CharlesBretana Kiedy czytam „przechowuj je oba”, myślę, że „redundancja” i „nie znormalizowane”, co równa się „Te rzeczy mogą się schrzanić” i „Muszę się upewnić, że obie zostaną zmienione, jeśli kiedykolwiek ulegną zmianie”. Jeśli masz nadmiarowość, powinien istnieć bardzo dobry powód (na przykład całkowicie niedopuszczalna wydajność), ponieważ nadmiarowość zawsze może spowodować niespójność danych.
jpmc26
3
@ jpmc26, Nie ma absolutnie ŻADNYCH problemów związanych z redundancją lub normalizacją. Klucz zastępczy nie ma sensownego połączenia z wartościami w kluczu naturalnym, więc nigdy nie należy go zmieniać. Jeśli chodzi o normalizację, o jakich problemach z normalizacją mówisz? Normalizacja dotyczy znaczących atrybutów relacji; wartość liczbowa klucza zastępczego (w istocie sama koncepcja klucza zastępczego) leży całkowicie poza kontekstem jakiejkolwiek normalizacji.
Charles Bretana
1
Odpowiadając na twoje inne pytanie, szczególnie dotyczące tabeli stanów, gdybyś miał w tej tabeli zastępczy klucz z wartościami, powiedzmy, od 1 do 50, ale NIE umieściłeś innego unikalnego indeksu lub klucza w kodzie pocztowym stanu, (i moim zdaniem również w nazwie stanu), co zatem powstrzymuje kogoś przed wpisaniem dwóch wierszy z różnymi zastępczymi wartościami klucza, ale z tym samym kodem pocztowym i / lub nazwą stanu? Jak aplikacja kliencka poradziłaby sobie z tym, gdyby były dwa wiersze z „NJ”, „New Jersey”? Natural Keys zapewnia spójność danych!
Charles Bretana
81

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.

Steve McLeod
źródło
25
Prawdziwe. Jedna z moich największych baz danych zawiera wpisy dotyczące Jugosławii i Związku Radzieckiego. Cieszę się, że to nie są klucze główne.
Paul Tomblin
8
@Steve, więc dlaczego ANSI SQL obsługuje składnię ON UPDATE CASCADE?
Bill Karwin,
5
Niezmienność nie jest wymogiem klucza. W każdym razie klucze zastępcze czasami się zmieniają. Nie ma nic złego w zmianie kluczy, jeśli zajdzie taka potrzeba.
nvogel
9
Paul, więc w swojej bazie danych zmieniłeś Związek Radziecki na Rosję? I udawać, że SU nigdy nie istnieje? A wszystkie odniesienia do SU wskazują teraz na Rosję?
Dainius
6
@alga Urodziłem się w SU, więc wiem, co to jest.
Dainius
52

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:

  • Procesor Intel® Core ™ i7-7500U przy 2,70 GHz × 4
  • 15,6 GiB RAM, z czego około 8 GB było wolne podczas testu.
  • Dysk SSD 148,6 GB z dużą ilością wolnego miejsca.
  • Ubuntu 16.04 64-bitowy
  • MySQL Ver 14.14 Distrib 5.7.20, dla systemu Linux (x86_64)

Stoły:

create table jan_int (data1 varchar(255), data2 int(10), myindex tinyint(4)) ENGINE=InnoDB;
create table jan_int_index (data1 varchar(255), data2 int(10), myindex tinyint(4), INDEX (myindex)) ENGINE=InnoDB;
create table jan_char (data1 varchar(255), data2 int(10), myindex char(6)) ENGINE=InnoDB;
create table jan_char_index (data1 varchar(255), data2 int(10), myindex char(6), INDEX (myindex)) ENGINE=InnoDB;
create table jan_varchar (data1 varchar(255), data2 int(10), myindex varchar(63)) ENGINE=InnoDB;
create table jan_varchar_index (data1 varchar(255), data2 int(10), myindex varchar(63), INDEX (myindex)) ENGINE=InnoDB;

Następnie wypełniłem 10 milionów wierszy w każdej tabeli skryptem PHP, którego istota jest następująca:

$pdo = get_pdo();

$keys = [ 'alabam', 'massac', 'newyor', 'newham', 'delawa', 'califo', 'nevada', 'texas_', 'florid', 'ohio__' ];

for ($k = 0; $k < 10; $k++) {
    for ($j = 0; $j < 1000; $j++) {
        $val = '';
        for ($i = 0; $i < 1000; $i++) {
            $val .= '("' . generate_random_string() . '", ' . rand (0, 10000) . ', "' . ($keys[rand(0, 9)]) . '"),';
        }
        $val = rtrim($val, ',');
        $pdo->query('INSERT INTO jan_char VALUES ' . $val);
    }
    echo "\n" . ($k + 1) . ' millon(s) rows inserted.';
}

W przypadku inttabel bit ($keys[rand(0, 9)])został zastąpiony justem rand(0, 9), a dla varchartabel 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;
  • Do jan_intstołu:
    • SELECT count(*) FROM jan_int WHERE myindex = 5;
    • SELECT BENCHMARK(1000000000, (SELECT count(*) FROM jan_int WHERE myindex = 5));
  • W przypadku innych tabel, tak samo jak powyżej, myindex = 'califo'dla chartabel i myindex = 'california'dla varchartabel.

Czasy BENCHMARKzapytania w każdej tabeli:

  • jan_int: 21.30 sek
  • jan_int_index: 18,79 sek
  • jan_char: 21.70 sek
  • jan_char_index: 18,85 sek
  • jan_varchar: 21,76 sek
  • jan_varchar_index: 18,86 sek

Jeśli chodzi o rozmiary tabel i indeksów, oto wynik show table status from janperformancetest;(bez kilku kolumn):

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name              | Engine | Version | Row_format | Rows    | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Collation              |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| jan_int           | InnoDB |      10 | Dynamic    | 9739094 |             43 |   422510592 |               0 |            0 |   4194304 |           NULL | utf8mb4_unicode_520_ci |  
| jan_int_index     | InnoDB |      10 | Dynamic    | 9740329 |             43 |   420413440 |               0 |    132857856 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_char          | InnoDB |      10 | Dynamic    | 9726613 |             51 |   500170752 |               0 |            0 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_char_index    | InnoDB |      10 | Dynamic    | 9719059 |             52 |   513802240 |               0 |    202342400 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_varchar       | InnoDB |      10 | Dynamic    | 9722049 |             53 |   521142272 |               0 |            0 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_varchar_index | InnoDB |      10 | Dynamic    | 9738381 |             49 |   486539264 |               0 |    202375168 |   7340032 |           NULL | utf8mb4_unicode_520_ci | 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

Mój wniosek jest taki, że nie ma różnicy w wydajności w tym konkretnym przypadku użycia.

Jan Żankowski
źródło
Wiem, że jest już późno, ale byłbym ciekawy wyników, gdybyś wybrał mniej idealną strunę dla stanu gdzie. „califo [rnia]” było idealne, ponieważ mogło odrzucić niedopasowania po porównaniu pierwszego znaku, wymagając jedynie dalszego sprawdzenia rzeczywistych dopasowań; coś w rodzaju „newham” dałoby bardziej interesujące wyniki, ponieważ nowością byłoby porównywanie większej liczby znaków w celu wyeliminowania wszystkich niedopasowań. Ponadto ograniczenie liczby całkowitych w ten sposób wiąże się z kumulacją szans przeciwko nim, dałbym im co najmniej 26 wartości.
Uueerdo
15
Niesamowite, że w pytaniu sprzed 10 lat jest to tylko jedna z dwóch odpowiedzi, które nie są tylko spekulacjami i opierają się na rzeczywistych testach porównawczych.
Adrian Baker
1
Ale twoje tabele nie mają klucza podstawowego, który w rzeczywistości w InnoDB jest posortowaną strukturą danych. Szybkość między sortowaniem liczb całkowitych a sortowaniem ciągów powinna być różna.
Melkor,
1
@Melkor Uczciwa uwaga, której używam INDEXzamiast PRIMARY KEY. Nie pamiętam swojego rozumowania - prawdopodobnie założyłem, że PRIMARY KEYjest to tylko INDEXograniczenie 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.
Jan Żankowski
1
Operacja wyszukiwania wymaga również porównań w polu klucza podstawowego (jak wyszukiwanie binarne), gdzie int powinno być nieco szybsze niż varchar. Ale jak sugerowały twoje eksperymenty, nie jest to takie oczywiste (a może dlatego, że nie miałeś klucza podstawowego, więc wszystkie zapytania były wolniejsze). Myślę, że to samo dotyczy wstawiania i wyszukiwania.
Melkor
38

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. ..

Charles Bretana
źródło
Pewnie? Masz większość formatów danych opartych na wierszach? Oprócz kluczy są jeszcze inne dane. Czy współczynnik 5 nie jest utopijny?
ManuelSchneid3r
1
@ manuelSchneid3r, co? utopijny? Nie, czynnik 5 nie jest „utopijny”. To tylko 20 podzielone przez 4. A co oznacza „oparty na wierszach formatu danych”? Indeksy nie są oparte na wierszach, są to zrównoważone struktury drzewiaste.
Charles Bretana
36

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ść.

Timothy Khouri
źródło
42
bez znaczenia, nie wiedząc, jak długie były varchary ... Jeśli miałyby szerokość 100 bajtów, to gwarantowane, że nie uzyskasz takiej samej wydajności jak 4-bajtowe int
Charles Bretana
6
Warto również wiedzieć, jakiej bazy danych używasz i jakiej wersji. Niemal zawsze pracowano nad dostrajaniem wydajności i ulepszano je z wersji na wersję.
Dave Black
VARCHAR zdecydowanie ma znaczenie dla rozmiaru indeksu. Indeks określa, ile można zmieścić w pamięci. A indeksy w pamięci są dużo, dużo szybsze niż te, które nie są. Możliwe, że dla twoich 10-metrowych wierszy masz 250 MB pamięci dostępnej dla tego indeksu i wszystko było w porządku. Ale jeśli masz 100 m rzędów, będziesz mniej dobrze w tej pamięci.
Paul Draper,
9

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.

Joel Coehoorn
źródło
2
Czy wiesz na pewno, że byłoby to drogie? A może tylko zgadujesz?
Steve McLeod
Oczywiście zależy to od implementacji rdbms, ale z tego, co rozumiem, większość serwerów zachowuje wartość skrótu rzeczywistej wartości do celów indeksowania. Mimo to, i nawet jeśli jest to stosunkowo krótki hash (powiedzmy, 10 bajtów), nadal jest więcej pracy, aby porównać 2 10-bajtowe skróty niż 2 4-bajtowe liczby całkowite.
Joel Coehoorn
NIGDY nie używaj długiego (szerokiego) klucza do łączenia ... Ale jeśli jest to najlepsza reprezentacja tego, co jest unikalne dla wierszy w tabeli, to lepiej, aby na liście znajdował się unikalny klucz (lub indeks - co jest tym samym) tabeli przy użyciu tych wartości naturalnych. Klucze nie służą do łączenia, możesz dołączyć do wszystkiego, czego dusza zapragnie. Klucze służą zapewnieniu spójności danych.
Charles Bretana
6

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:

ex.
id value
1 A
2 B
3 C

Update 3 to D
id value
1 A
2 B
3 D

Update 2 to C
id value
1 A
2 C
3 D

Update 3 to B
id value
1 A
2 C
3 B

Wszystko zależy od tego, o co naprawdę musisz się martwić w swojej strukturze i co oznacza najbardziej.

LeppyR64
źródło
3

Typowe przypadki, w których surogat AUTO_INCREMENTboli:

Typowym wzorcem schematu jest mapowanie wiele do wielu :

CREATE TABLE map (
    id ... AUTO_INCREMENT,
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(id),
    UNIQUE(foo_id, bar_id),
    INDEX(bar_id) );

Wydajność tego wzorca jest znacznie lepsza, szczególnie w przypadku korzystania z InnoDB:

CREATE TABLE map (
    # No surrogate
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(foo_id, bar_id),
    INDEX      (bar_id, foo_id) );

Czemu?

  • Dodatkowe klucze InnoDB wymagają dodatkowego wyszukiwania; przesuwając parę do PK, którego unika się w jednym kierunku.
  • Indeks pomocniczy jest „pokrywający”, więc nie wymaga dodatkowego wyszukiwania.
  • Ta tabela jest mniejsza z powodu pozbycia się idi jednego indeksu.

Inny przypadek ( kraj ):

country_id INT ...
-- versus
country_code CHAR(2) CHARACTER SET ascii

Zbyt często nowicjusz normalizuje kod_krajowy do 4-bajtowego, INTzamiast używać „naturalnego” 2-bajtowego, prawie niezmiennego 2-bajtowego ciągu. Szybsze, mniejsze, mniej JOIN, bardziej czytelne.

Rick James
źródło
2

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.

Herman J. Radtke III
źródło
2

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:

+----------+          +---------+
| Accident |>--------<| Vehicle |
+-----v----+ 1      * +----v----+
     1|                    |1
      |    +----------+    |
      +---<| Casualty |>---+
         * +----------+ *

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.

Diego Duarte
źródło
1
Wygląda na to, że musisz przyjrzeć się partycjonowaniu.
jcoffland
2

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.

Volksman
źródło
0

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.

Dexygen
źródło
0

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ę.

Vinod
źródło
0

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)

Shadi Namrouti
źródło