MySQL - longueur et performances varchar

19

La déclaration de la VARCHARtaille est-elle logique pour les performances? Y a-t-il une différence (de vitesse) entre VARCHAR(50)et VARCHAR(255)? Ou définir la longueur est une contrainte logique / de conception?

Sonique
la source

Réponses:

31

Il s'agit d'une «question d'examen / entretien» très courante. Je répondrai aussi bien que possible:

Dans les formats de ligne standard pour InnoDB et MyISAM (dynamique / compact) a VARCHAR(50)et a VARCHAR(255)stockeront le texte de la chaîne de la même manière - 1 octet pour la longueur et la chaîne réelle avec entre 1 et 4 octets par caractère (selon l'encodage et le caractère réel stocké).

En fait, si je me souviens bien, je me souviens que quelqu'un avait modifié le dictionnaire de données avec un éditeur hexadécimal afin de changer quelque chose comme un VARCHAR(50)en un VARCHAR(100), donc cela pourrait être fait dynamiquement (normalement, cela nécessite une reconstruction de table). Et cela était possible, car les données réelles n'étaient pas affectées par ce changement.

Ce n'est pas vrai avec VARCHAR(256), car alors 2 octets (au moins) pour la longueur sont toujours requis.

Donc, cela signifie que nous devons toujours faire VARCHAR(255), ne devrions - nous? Non, il y a plusieurs raisons.

Bien qu'InnoDB puisse stocker un varchar de manière dynamique, cela n'est pas vrai pour les autres moteurs. MyISAM a un format de taille de ligne fixe et les tables MEMORY sont toujours de taille fixe. Faut-il se soucier de ces autres moteurs? Oui, nous devrions le faire, car même si nous ne les utilisons pas directement, les tables MEMORY sont très couramment utilisées pour les résultats intermédiaires (tables temporaires en mémoire) , et comme les résultats ne sont pas connus au préalable, la table doit être créée avec la taille maximale possible - VARCHAR(255)si tel est notre type. Si vous pensez à l'espace gaspillé, si nous utilisons l' 'utf8' charsetencodage de MySQL , MEMORY réservera 2 octets pour la longueur + 3 * 255 octets par ligne(pour les valeurs qui ne peuvent prendre que quelques octets sur InnoDB). Cela représente presque 1 Go sur une table d'un million - uniquement pour le VARCHAR. Non seulement cela entraîne un stress mémoire inutile, mais cela peut provoquer les actions à effectuer sur le disque, ce qui peut le ralentir des milliers de fois. Tout cela en raison d'une mauvaise sélection de son type de données défini (indépendamment du contenu).

Cela a également des conséquences pour InnoDB. La taille de l'index est limitée à 3072 octets et les index à colonne unique, à 767 octets *. Il est donc très probable que vous ne pourrez pas indexer entièrement unVARCHAR(255) champ (en supposant que vous utilisez utf8 ou tout autre encodage de longueur variable).

De plus, la taille maximale des lignes en ligne pour InnoDB est d'une demi-page (environ 8000 octets) et des champs de longueur variable comme BLOB ou varchar peuvent être stockés hors page s'ils ne tiennent pas sur la demi-page . Cela a des conséquences sur les performances (parfois bonnes, parfois mauvaises, selon l'utilisation) qui ne peuvent être ignorées. Cela a provoqué une certaine bizarrerie entre les formats COMPACT et DYNAMIC. Voir, par exemple: erreur 1118: taille de ligne trop grande. utf8 innodb

Dernier point mais non le moindre, comme @ypercube me l'a rappelé, plus de 1 octet pour la longueur peut être requis même si vous utilisez VARCHAR(255), car la définition est en caractères, tandis que la longueur stocke des octets. Par exemple, REPEAT('ñ', 255)a plus de 2 ^ 255 octets dans utf8, il faudrait donc plus de 1 octet pour stocker sa longueur:

mysql> SELECT LENGTH(REPEAT('ñ', 255));
+---------------------------+
| LENGTH(REPEAT('ñ', 255))  |
+---------------------------+
|                       510 |
+---------------------------+
1 row in set (0.02 sec)

mysql> SELECT CHAR_LENGTH(REPEAT('ñ', 255));
+--------------------------------+
| CHAR_LENGTH(REPEAT('ñ', 255))  |
+--------------------------------+
|                            255 |
+--------------------------------+
1 row in set (0.00 sec)

Donc, le conseil général est d' utiliser le plus petit type possible , car cela peut potentiellement créer des problèmes de performances ou de gestion autrement. A VARCHAR(100)vaut mieux que VARCHAR(255)(même si a VARCHAR(20)serait mieux), même si vous ne connaissez pas la longueur exacte. Essayez d'être prudent car, à moins que le tableau ne soit trop volumineux, vous pouvez toujours modifier la définition ultérieurement.

Mise à jour: En raison de la popularité explosive des chaînes de longueur variable, par exemple, avec l'utilisation des emojis, Oracle a fait pression pour améliorer les performances de ces cas. Dans les dernières versions de MySQL (5.6, 5.7), InnoDB a été défini comme moteur par défaut pour les tables temporaires intrinsèques et explicites, ce qui signifie que les champs de longueur variable sont désormais des citoyens de première classe. Cela signifie qu'il peut y avoir moins de raisons d'avoir des longueurs de caractères très limitées (mais celles-ci existent toujours).

(*) Deuxième mise à jour : large_prefix_index est désormais activé par défaut sur les dernières versions de MySQL (8.0), mais cela reste vrai pour les anciennes versions ou si vous utilisez des formats de fichiers / lignes innodb lagacy (autres que dynamiques ou compressés), mais maintenant par défaut, les index à colonne unique peuvent aller jusqu'à ces 3072 octets.

jynus
la source
petite mise à jour: MySQL-8.0.13 + utilise TempTable par défaut pour les tables temporaires qui ont un stockage efficace pour les varchars.
danblack
0

Oubliez le préfixe de 1 contre 2 octets VARCHARs.

  • Il a un impact minime sur les performances.
  • Il est "2" plus souvent que ne le dit la règle évidente.

La question sur 255 a été posée et a répondu plusieurs fois.

  • Trop de temps VARCHARspeut conduire à l'échec de CREATE TABLE.
  • Les tables temporaires peuvent se transformer en MEMORYtables, avec VARCHARstransformées en VARCHAR. Cela signifie, par exemple, que VARCHAR(255) CHARACTER SET utf8mb4veut une longueur fixe de 1020 octets. (Cela échouera et dégénérera en utilisant MyISAM.)

Conclusion: n'utilisez pas aveuglément 255 (ou 256); faire ce qui est logique pour le schéma.

Rick James
la source