web-dev-qa-db-fra.com

Existe-t-il une différence de performance réelle entre les clés primaires INT et VARCHAR?

Existe-t-il une différence de performance mesurable entre l'utilisation de INT et de VARCHAR en tant que clé primaire dans MySQL? J'aimerais utiliser VARCHAR comme clé primaire pour les listes de références (pensez aux États-Unis, codes de pays) et un collègue ne bougera pas sur INT AUTO_INCREMENT en tant que clé primaire pour toutes les tables. 

Mon argument, tel que détaillé ici , est que la différence de performances entre INT et VARCHAR est négligeable, car chaque référence de clé étrangère INT nécessite un JOIN pour donner un sens à la référence, une clé VARCHAR présentant directement les informations.

Alors, est-ce que quelqu'un a une expérience de ce cas d'utilisation particulier et des problèmes de performances associés?

147
Jake McGraw

Vous dites que vous pouvez éviter un certain nombre de requêtes jointes en utilisant ce qu'on appelle une clé natural au lieu d'une clé de substitution . Vous êtes le seul à pouvoir évaluer si l’avantage de cet avantage est significatif pour votre application. 

Autrement dit, vous pouvez mesurer les requêtes les plus importantes dans votre application, car elles fonctionnent avec de gros volumes de données ou sont exécutées très fréquemment. Si ces requêtes ont l'avantage d'éliminer une jointure et qu'elles ne souffrent pas de l'utilisation d'une clé primaire varchar, faites-le.

N'utilisez aucune de ces stratégies pour toutes les tables de votre base de données. Il est probable que dans certains cas, une clé naturelle est préférable, mais dans d'autres cas, une clé de substitution est préférable. 

D'autres personnes font valoir qu'il est rare en pratique qu'une clé naturelle ne change jamais ou ait des doublons, aussi les clés de substitution en valent-elles la peine.

69
Bill Karwin

Ce n'est pas une question de performance. Il s'agit de ce qui fait une bonne clé primaire. Unique et immuable dans le temps. Vous pensez peut-être qu'une entité telle qu'un code de pays ne change jamais au fil du temps et serait un bon candidat pour une clé primaire. Mais l'expérience amère est rarement le cas.

INT AUTO_INCREMENT remplit la condition "unique et invariable dans le temps". D'où la préférence.

73
Steve McLeod

Dépend de la longueur. Si varchar contient 20 caractères et que int est 4, alors si vous utilisez un int, votre index aura CINQ fois plus de nœuds par page d'espace d'index sur le disque ... Cela signifie que l'index nécessitera un cinquième de plus de lectures physiques et/ou logiques. 

Donc, si les performances sont un problème, compte tenu de l'occasion, utilisez toujours une clé non significative intégrale (appelée un substitut) pour vos tables et pour les clés étrangères qui référencent les lignes de ces tables ... 

En même temps, pour garantir la cohérence des données, chaque table, là où il importe, devrait aussi _ avoir une clé alternative non numérique significative (ou un index unique) afin de garantir la duplication des lignes. ne peut pas être inséré (dupliquer en fonction d'attributs de table significatifs). 

Pour l'utilisation spécifique dont vous parlez (comme les recherches d'état), cela n'a pas vraiment d'importance, car la taille de la table est si petite. En général, les performances des index sur des tables contenant moins de quelques milliers de lignes n'ont aucun impact. .. 

33
Charles Bretana

Absolument pas.

J'ai effectué plusieurs ... plusieurs ... contrôles de performances entre INT, VARCHAR et CHAR.

10 millions de tables d’enregistrement avec une clé primaire (unique et en cluster) ont exactement la même vitesse et les mêmes performances (et le coût des sous-arbres) quel que soit le modèle que j’ai utilisé.

Cela étant dit ... utilisez ce qui convient le mieux à votre application. Ne vous inquiétez pas pour la performance.

30
Timothy Khouri

Pour les codes abrégés, il n'y a probablement pas de différence. Cela est d'autant plus vrai que la table contenant ces codes est susceptible d'être très petite (quelques milliers de lignes au plus) et de ne pas changer souvent (quand est la dernière fois que nous avons ajouté un nouvel État américain).

Cela peut être dangereux pour les tables plus volumineuses avec une plus grande variation entre les clés. Pensez par exemple à utiliser une adresse électronique/un nom d'utilisateur à partir d'une table d'utilisateurs. Que se passe-t-il lorsque vous avez quelques millions d'utilisateurs et que certains d'entre eux ont des noms longs ou des adresses électroniques? Maintenant, chaque fois que vous avez besoin de joindre cette table avec cette clé, cela devient beaucoup plus coûteux.

9
Joel Coehoorn

En ce qui concerne la clé primaire, ce qui rend physiquement une ligne unique doit être déterminé comme clé primaire. 

Pour une référence en tant que clé étrangère, utiliser un entier auto-incrémenté comme substitut est une bonne idée pour deux raisons principales.
- Premièrement, il y a généralement moins de frais généraux dans la jointure.
- Deuxièmement, si vous devez mettre à jour la table qui contient l’unique varchar, la mise à jour doit ensuite s’adapter à toutes les tables enfants et les mettre à jour, ainsi que les index. table principale et ses index.

Le problème avec l'utilisation de la mère porteuse est que vous pouvez éventuellement permettre de changer le sens de la mère porteuse:

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

Tout dépend de ce dont vous avez réellement besoin dans votre structure et de ce qui signifie le plus.

6
LeppyR64

Cas courants où un substitut AUTO_INCREMENT fait mal:

Un modèle de schéma courant est un mappage plusieurs à plusieurs :

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

Les performances de ce modèle sont bien meilleures, en particulier avec InnoDB:

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

Pourquoi?

  • Les clés secondaires InnoDB nécessitent une recherche supplémentaire; en déplaçant la paire dans le PK, cela est évité pour une direction.
  • L'index secondaire est "couvrant", il n'a donc pas besoin de la recherche supplémentaire.
  • Ce tableau est plus petit en raison de la suppression de id et d'un index.

Un autre cas ( pays ):

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

Trop souvent, le novice normalise country_code en une variable INT de 4 octets au lieu d'utiliser une chaîne de 2 octets «naturelle», presque invariable. Plus rapide, plus petit, moins de jointures, plus lisible.

2
Rick James

La question concerne MySQL alors je dis qu'il y a une différence significative. S'il s'agissait d'Oracle (qui stocke les nombres sous forme de chaîne - oui, je ne pouvais pas le croire au début), alors il n'y a pas beaucoup de différence.

Le stockage dans la table n’est pas le problème mais la mise à jour et la référence à l’index l’est. Les requêtes impliquant la recherche d'un enregistrement en fonction de sa clé primaire sont fréquentes - vous voulez qu'elles se produisent le plus rapidement possible car elles se produisent souvent.

La chose est un CPU traite naturellement avec 4 et 8 octets entiers, dans silicium . C’est VRAIMENT rapide de comparer deux nombres entiers - cela se produit en un ou deux cycles d’horloge. 

Maintenant, regardez une chaîne - elle est composée de nombreux caractères (plus d'un octet par caractère ces jours-ci). La comparaison de deux chaînes pour la priorité ne peut pas être effectuée en un ou deux cycles. Au lieu de cela, les caractères des chaînes doivent être itérés jusqu'à ce qu'une différence soit trouvée. Je suis sûr qu'il existe des astuces pour accélérer les choses dans certaines bases de données, mais ce n'est pas pertinent ici car une comparaison int est faite naturellement et très rapidement dans le silicium par le processeur.

Ma règle générale - chaque clé primaire doit être une INT auto-incrémentée, en particulier dans les applications OO utilisant un ORM (Hibernate, Datanucleus, etc.) où il existe de nombreuses relations entre les objets - elles sont généralement toujours implémentées sous forme de FK la capacité de la base de données à résoudre ces problèmes rapidement est importante pour la réactivité de votre application.

2
Volksman

Chez HauteLook, beaucoup de nos tables ont été modifiées pour utiliser des clés naturelles. Nous avons constaté une augmentation réelle des performances. Comme vous l'avez mentionné, beaucoup de nos requêtes utilisent maintenant moins de jointures, ce qui les rend plus performantes. Nous utiliserons même une clé primaire composite si cela a du sens. Cela étant dit, certaines tables sont simplement plus faciles à utiliser si elles ont une clé de substitution.

En outre, si vous laissez des personnes écrire des interfaces dans votre base de données, une clé de substitution peut être utile. La tierce partie peut compter sur le fait que la clé de substitution ne changera que dans de très rares circonstances.

2

J'ai fait face au même dilemme. J'ai réalisé un schéma DW (Constellation Schema) avec 3 tables de faits, accidents de la route, véhicules en accidents et pertes en accidents en accidents. Les données incluent tous les accidents enregistrés au Royaume-Uni de 1979 à 2012 et 60 tableaux de dimensions. Au total, environ 20 millions de disques.

Relations entre les tables de faits:

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

RDMS: MySQL 5.6

Nativement, l’indice d’accident est un varchar (chiffres et lettres), composé de 15 chiffres. J'ai essayé de ne pas avoir de clé de substitution, une fois que les index d'accident ne changeraient jamais . Sur un ordinateur i7 (8 cœurs), le DW devenait trop lent pour interroger après 12 millions d'enregistrements de charge en fonction des dimensions . Après beaucoup de remaniement et l’ajout de clés de substitution bigint j’ai obtenu une augmentation moyenne des performances de vitesse de 20% . Je travaille dans le réglage et la mise en cluster de MySQL.

1
Diego Duarte

Permettez-moi de dire oui, il y a bien une différence, compte tenu de l'étendue de la performance (définition standard):

1- L'utilisation de la substitution int est plus rapide dans l'application car vous n'avez pas besoin d'utiliser ToUpper (), ToLower (), ToUpperInvarient () ou ToLowerInvarient () dans votre code ou dans votre requête, ces 4 fonctions ont des performances différentes. Voir les règles de performance de Microsoft à ce sujet. (performance de l'application)

2- L'utilisation de la substitution int garantit de ne pas changer la clé dans le temps. Même les codes de pays peuvent changer, voir Wikipedia comment les codes ISO ont changé au fil du temps. Cela prendrait beaucoup de temps pour changer la clé primaire des sous-arbres. (performance de la maintenance des données)

3- Il semble qu'il y ait des problèmes avec les solutions ORM, telles que NHibernate lorsque PK/FK n'est pas int. (performance du développeur)

0
Shadi Namrouti

Pas sûr des implications en termes de performances, mais il semble qu'un compromis possible, du moins pendant le développement, consisterait à inclure à la fois la clé "de substitution" entière, auto-incrémentée, ainsi que la clé naturelle, unique et souhaitée. Cela vous donnerait l'occasion d'évaluer les performances, ainsi que d'autres problèmes éventuels, notamment la possibilité de modification des clés naturelles.

0
George Jempty

Comme d'habitude, il n'y a pas de réponse générale. 'Ça dépend!' et je ne suis pas facétieux. Ma compréhension de la question initiale concernait les clés de petites tables, telles que Pays (identifiant entier ou code de caractère/varchar) étant une clé étrangère vers une table potentiellement énorme, telle que la table adresse/contact.

Il existe deux scénarios dans lesquels vous souhaitez récupérer les données de la base de données. Tout d’abord, il s’agit d’une requête de type liste/recherche dans laquelle vous souhaitez répertorier tous les contacts avec des codes ou des noms d’État et de pays (les identifiants ne vous aideront pas et nécessiteront donc une recherche). L'autre est un scénario d'obtention sur la clé primaire qui montre un enregistrement de contact unique où le nom de l'état, du pays doit être affiché.

Pour ces derniers, peu importe sur quoi est basé le FK puisque nous rassemblons des tables pour un ou plusieurs enregistrements et sur des lectures clés. Le scénario précédent (recherche ou liste) peut être affecté par notre choix. Puisqu'il est nécessaire de montrer pays (au moins un code reconnaissable et peut-être même la recherche inclut un code de pays), ne pas avoir à joindre une autre table via une clé de substitution peut potentiellement (je suis juste prudent ici parce que je n'ai pas encore testé ceci, mais cela semble hautement probable) améliore les performances; malgré le fait que cela aide certainement à la recherche.

Comme les codes sont de petite taille - pas plus de 3 caractères, généralement pour le pays et l’état, il peut être correct d’utiliser les clés naturelles comme clés étrangères dans ce scénario.

L'autre scénario où les clés dépendent de valeurs varchar plus longues et peut-être de tables plus volumineuses; la clé de substitution a probablement l'avantage.

0
Vinod