web-dev-qa-db-fra.com

MySQL - longueur et performances varchar

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

26
Sonique

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), une VARCHAR(50) et une VARCHAR(255) stockera 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 VARCHAR(50) en 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), n'est-ce pas? 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 à l'avance, la table doit être créée avec la taille maximale possible -VARCHAR(255) si c'est notre type. Si vous pouvez penser à l'espace perdu, si nous utilisons le codage 'utf8' charset 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 *. Donc, il est très probable que vous ne pourrez pas indexer entièrement un champ VARCHAR(255) (en supposant que vous utilisez utf8 ou tout autre autre codage de longueur variable).

De plus, la taille maximale des lignes en ligne pour InnoDB est d'une demi-page (environ 8000 octets) et les 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, donc il faudrait 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 consiste à utiliser le plus petit type possible , car cela peut potentiellement créer des problèmes de performances ou de gestion autrement. Une VARCHAR(100) est meilleure que VARCHAR(255) (bien qu'une 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 grand, 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 d'emojis, Oracle a fait pression pour améliorer les performances dans 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 utilisent des formats de fichiers/lignes innodb lagacy (autres que dynamiques ou compressés), mais maintenant, par défaut, les index à colonne unique peuvent atteindre ces 3072 octets.

35
jynus

Oubliez le préfixe de 1 contre 2 octets sur 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.

  • Un nombre trop long de VARCHARs peut entraîner l'échec de CREATE TABLE.
  • Un SELECT complexe utilisera une table temporaire pour, par exemple, faire le tri d'un ORDER BY. Une table MEMORY est utilisée dans certaines situations. Dans d'autres situations, MyISAM sera utilisé. Lorsque vous utilisez MEMORY, VARCHARs est transformé en CHARs (pour la table temporaire). Cela signifie, par exemple, que VARCHAR(255) CHARACTER SET utf8mb4 veut une longueur fixe de 1020 octets. Et c'est "trop ​​grand" pour utiliser MEMORY, il passera donc à MyISAM, moins efficace.

(Dans MySQL 8.0, les détails des tables temporaires ont changé. Le paragraphe précédent s'applique aux tables de tous les moteurs avant 8.0.)

Conclusion: n'utilisez pas aveuglément 255 (ou 256); faire ce qui est logique pour le schéma. Si vous avez besoin de 255 (ou 1024 ou autre), continuez à l'utiliser. Je signale simplement quelques inconvénients.

Quelle performance a atteint? C'est difficile à prévoir; en général, cela ne vaut pas la peine de s'inquiéter. (La question portait sur les performances, j'ai essayé de répertorier tous les cas où le nombre dans VARCHAR est important, même un peu.)

0
Rick James