Quel est l'impact sur les performances de l'utilisation de CHAR vs VARCHAR sur un champ de taille fixe?

58

J'ai une colonne indexée qui stocke un hachage MD5. Ainsi, la colonne stockera toujours une valeur de 32 caractères. Pour une raison quelconque, cela a été créé comme un varchar plutôt que d'un caractère. Vaut-il la peine de migrer la base de données pour la convertir en caractère? Ceci est dans MySQL 5.0 avec InnoDB.

Jason Baker
la source
6
AVERTISSEMENT Cette question et ses réponses ont été écrites avant InnoDB et utf8 étaient les valeurs par défaut.
Rick James

Réponses:

56

Une question similaire avait déjà été posée

Conséquences sur les performances des tailles MySQL VARCHAR

Voici l'extrait de ma réponse

Vous devez réaliser les compromis liés à l'utilisation de CHAR vs VARCHAR.

Avec les champs CHAR, ce que vous allouez correspond exactement à ce que vous obtenez. Par exemple, CHAR (15) alloue et stocke 15 octets, quelle que soit la manière dont les caractères sont placés dans le champ. La manipulation des chaînes est simple et directe car la taille du champ de données est totalement prévisible.

Avec les champs VARCHAR, vous obtenez une histoire complètement différente. Par exemple, VARCHAR (15) attribue de manière dynamique jusqu'à 16 octets, jusqu'à 15 pour les données et au moins un octet supplémentaire pour stocker la longueur des données. Si vous avez la chaîne 'hello' à stocker qui prendra 6 octets, pas 5. La manipulation de chaîne doit toujours effectuer une vérification de la longueur dans tous les cas.

Le compromis est plus évident lorsque vous faites deux choses: 1. Stocker des millions ou des milliards de lignes 2. Indexer des colonnes qui sont CHAR ou VARCHAR

TRADEOFF # 1 Évidemment, VARCHAR conserve l’avantage, car des données de longueur variable produiraient des lignes plus petites et donc des fichiers physiques plus petits.

TRADEOFF # 2 Etant donné que les champs CHAR nécessitent moins de manipulation de chaîne en raison de la largeur fixe des champs, les recherches d'index par rapport au champ CHAR sont en moyenne 20% plus rapides que celles des champs VARCHAR. Ce n'est pas une conjecture de ma part. Le livre MySQL Database Design and Tuning a prouvé quelque chose de merveilleux sur une table MyISAM. L'exemple dans le livre fait quelque chose comme ceci:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Cette directive oblige tous les VARCHAR à se comporter comme des chars. En 2007, lors de mon travail précédent, je m'étais occupé de cette tâche en prenant une table de 300 Go et en accélérant les recherches d'index de 20%, sans rien changer d'autre. Cela a fonctionné comme publié. Cependant, il a produit une table presque deux fois plus grande, mais cela revient simplement au compromis # 1.

Vous pouvez analyser les données stockées pour voir ce que MySQL recommande pour la définition des colonnes. Il suffit de lancer ce qui suit contre n’importe quelle table:

SELECT * FROM tblname PROCEDURE ANALYSE();

Cela traversera la table entière et recommandera des définitions de colonne pour chaque colonne en fonction des données qu'elle contient, des valeurs de champ minimales, des valeurs de champs maximales, etc. Parfois, il suffit de faire preuve de bon sens lors de la planification de CHAR vs VARCHAR. Voici un bon exemple:

Si vous stockez des adresses IP, le masque d'une telle colonne contient au maximum 15 caractères (xxx.xxx.xxx.xxx). Je sauterais droit au CHAR(15)coeur car les longueurs d'adresses IP ne varieront pas beaucoup et la complexité supplémentaire de la manipulation de chaîne contrôlée par un octet supplémentaire. Vous pouvez toujours faire un PROCEDURE ANALYSE()contre une telle colonne. Il peut même recommander VARCHAR. Mon argent serait toujours sur CHAR sur VARCHAR dans ce cas.

Les problèmes CHAR vs VARCHAR ne peuvent être résolus que par une planification appropriée. Avec un grand pouvoir vient une grande responsabilité (cliché mais vrai).

MISE À JOUR

En ce qui concerne MD5, le calcul de l’ strleninterne doit être éliminé lors du changement de format de ligne. Il ne serait pas nécessaire de changer la définition du champ.

Si la clé MD5 est le seul présent de VARCHAR, j'y retournerais et convertirais le format de rangée de tableau en corrigé . S'il y a un nombre significatif d'autres champs VARCHAR présents, ils en bénéficieront également. En échange, la table s'élargirait à environ deux fois sa taille. Mais les requêtes devraient accélérer d'environ 20% de plus sans réglage supplémentaire.

RolandoMySQLDBA
la source
1
Je pense que je voudrais utiliser un caractère (4) ou quelque chose comme un entier non signé pour une adresse IP
Jack Douglas
@ JackPDouglas Vous avez raison sur ce point.
RolandoMySQLDBA
Les index ne sont-ils pas stockés avec une longueur fixe de toute façon? Je ne comprends pas comment changer le format de stockage en longueur fixe améliore la recherche dans l'index. Voulez-vous dire qu'il améliore les analyses de table?
Marcus Adams
1
@ JackDouglas, pourquoi pas bitet binary?
Pacerier
@Pacerier ça irait mieux, je suis d'accord :)
Jack Douglas
19

Il semble que vous économisiez 1 octet par valeur ou environ 3% en convertissant en a char. Cela ne vaut probablement pas la peine si vous stockez MD5 au format hexadécimal de toute façon - vous pourriez économiser 50% en utilisant un binary.

Merci à Ovais (voir commentaires) de remarquer que char(32)peut utiliser beaucoup plus de 32 octets si vous utilisez un jeu de caractères multi - octets.

Merci à Rick James pour avoir signalé que vous devriez utiliser la unhexfonction pour convertir la chaîne hexadécimale en binaire:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| longueur (bar) |
| ----------: |
| 32 |
| 16 |

db <> fiddle ici

Jack Douglas
la source
Bon appel à passer en binaire.
RThomas
Je prévois de convertir cela en un binaire. Maintenant que j'y pense, la taille ne devrait pas être différente, que j'utilise un octet ou un caractère, car notre encodage est utf-8. Ou ai-je tort?
Jason Baker
@Jason - l'encodage ne s'applique pas binary- ou ai-je mal compris?
Jack Douglas
3
pour une colonne char (32) avec un jeu de caractères utf-8, chaque valeur nécessiterait 32x3 octets pour son stockage. Pourquoi auriez-vous besoin de définir la valeur de hachage MD5 sur utf-8. La conversion en binaire (32) nécessiterait 32 octets par valeur.
ovais.tariq
1
Changer en BINARYfait très peu, sauf si vous utilisez également UNHEX(). C'est, vous pouvez stocker UNHEX(MD5(x))dans un 16 octets BINARY(16)pour économiser l' espace de stockage important par rapport MD5(x)à CHAR(32) CHARACTER SET ascii.
Rick James
15

Cela ne vaut pas la peine de changer à mon avis. Si vous parcourez la documentation, cela devrait illustrer la différence entre les deux. Dans votre scénario d'utilisation, l'un n'offre pas d'avantage significatif par rapport à l'autre, à moins que vous ne soyez vraiment préoccupé par la surcharge supplémentaire liée à la taille de la ligne.

http://dev.mysql.com/doc/refman/5.0/en/char.html

Notez également le premier commentaire sur la documentation que je vous renvoie ci-dessus ... "CHAR n'accélérera votre accès que si l'intégralité de l'enregistrement est de taille fixe. Autrement dit, si vous utilisez un objet de taille variable, vous pouvez tout aussi bien le faire. taille variable. Vous ne gagnez pas en vitesse en utilisant un CHAR dans une table qui contient également un VARCHAR "

RThomas
la source
Cette "accélération" s'applique à MyISAM, pas à InnoDB.
Rick James