Meilleures pratiques d'évaluation de longueur MySQL VARCHAR

Votre VARCHAR est-il de la bonne longueur ?

Auteur : Guan Yongqiang, membre de l'équipe DBA d'Aikesheng, possède de bonnes compétences en matière d'exploitation et de maintenance de MySQL. Il adore apprendre de nouvelles connaissances et est également un otaku qui adore jouer à des jeux.

Auteur : Li Fuqiang, membre de l'équipe DBA d'Aikesheng, familier avec MySQL, TiDB, OceanBase et d'autres bases de données. Je crois que si vous continuez à bien faire les bonnes choses, vous obtiendrez différentes récompenses.

Produit par la communauté open source Aikeson. Le contenu original ne peut être utilisé sans autorisation. Veuillez contacter l'éditeur et indiquer la source pour la réimpression.

Cet article compte environ 2 200 mots et sa lecture devrait prendre 8 minutes.

Description du contexte

Certains clients ont signalé avoir augmenté la longueur d' un champ de type VARCHAR . La première fois, vous pouvez le modifier rapidement, mais la deuxième fois, son exécution prend beaucoup de temps. Je suis confus car la quantité de données dans le tableau est presque la même. Pourquoi est-il plus rapide VARCHAR(20)d'ajuster de à , mais cela prend beaucoup de temps d' ajuster de à ?VARCHAR(50)VARCHAR(50)VARCHAR(100) Nous avons donc reproduit la situation et procédé à une analyse du problème.

informations environnementales

Les produits et les informations de version impliqués dans cette vérification sont les suivants :

produit Version
MySQL Serveur de communauté MySQL 5.7.25-log (GPL)
Banc système banc système 1.0.17

Récurrence de la scène

3.1 Préparation des données

mysql> show create table test.sbtest1;
+---------+----------------------------------------+
| Table   | Create Table                           |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select count(*) from test.sbtest1;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.10 sec)

3.2 Vérification du problème

Pour simuler la description du client, nous cmodifions le champ, VARCHAR(20)le modifions en VARCHAR(50)puis le modifions en VARCHAR(100), et observons le temps nécessaire à son exécution. Voici les commandes d'opération pertinentes et les résultats d'exécution :

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+--------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100);
Query OK, 1000000 rows affected (4.80 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+---------------------------+
| Table   | Create Table              |
+---------+---------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

Grâce à la vérification, il a été constaté que le problème réapparaîtrait de manière stable, nous avons donc continué à essayer de le modifier. Enfin, nous avons constaté que la modification prenait beaucoup de temps VARCHAR(63), VARCHAR(64)mais après 64 ans, nous avons continué à augmenter la longueur et avons constaté que c'était le cas. pourrait être réalisé rapidement.

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64);
Query OK, 1000000 rows affected (4.87 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+---------------+
| Table   | Create Table  |
+---------+---------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(65);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(66);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

3.3 Analyse du problème

Analyser la situation où il faut beaucoup de temps pour VARCHAR(63)la modifier . VARCHAR(64)En consultant la documentation officielle , nous avons constaté que VARCHARle type de caractère pouvant stocker des caractères lorsque la longueur d'octet est de 1 est 0~255. Le type de jeu de caractères actuel est UTF8MB4. Étant donné que UTF8MB4 est un jeu de caractères codés sur quatre octets, c'est-à-dire qu'un octet peut stocker 63,75 (255/4) caractères, donc lorsque nous le modifions enVARCHAR(63) , nous devons ajouter un octet pour les données. VARCHAR(64)Pour le stockage, il est nécessaire de compléter cette expansion de longueur en établissant une table temporaire, cela prend donc beaucoup de temps.

Vérification étendue

4.1 Préparation des données

mysql> show create table test_utf8.sbtest1;
+---------+----------------------------------------+
| Table   | Create Table                           |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(20) NOT NULL DEFAULT '',
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+------------------+
1 row in set (0.00 sec)

mysql> select count(*) from test_utf8.sbtest1;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.10 sec)

4.2 Vérification de la scène UTF8

Étant donné que UTF8 est un jeu de caractères codés sur trois octets, un octet peut stocker 85 (255/3 = 85) caractères.

La séquence de cette modification est : VARCHAR(20)→VARCHAR(50)→VARCHAR(85), et respectez le temps requis pour son exécution. Voici les commandes d'opération et les résultats d'exécution pertinents :

mysql>  ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(85) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(85) DEFAULT NULL,
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)

Séquence de modification : VARCHAR(85)→VARCHAR(86)→VARCHAR(100) A ce moment, nous observerons que l'instruction SQL exécutée renvoie directement une erreur. Nous supprimons donc algorithm=inplace ,lock=noneces deux paramètres, ce qui permet à ce SQL de créer une table temporaire et de verrouiller la table cible, puis de réexécuter le SQL. Voici les commandes d'opération et les résultats d'exécution pertinents :

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86);
Query OK, 1000000 rows affected (4.94 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table   | Create Table                  |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(86) DEFAULT NULL,
  `pad` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.3 Vérification du scénario UTF8MB4

Étant donné que UTF8MB4 est un jeu de caractères codés sur quatre octets, un octet peut stocker 63 (255/4 = 63,75) caractères.

La séquence de cette modification est : VARCHAR(20)→VARCHAR(50)→VARCHAR(63), et respectez le temps requis pour son exécution. Voici les commandes d'opération et les résultats d'exécution pertinents :

mysql>  ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+-------------------------+
| Table   | Create Table           |
+---------+-------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(63) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)

La séquence de cette modification est : VARCHAR(63)→VARCHAR(64)→VARCHAR(100). A ce moment, nous observerons que l'instruction SQL exécutée renvoie directement une erreur. Nous supprimons donc algorithm=inplace, lock=noneces deux paramètres, ce qui permet à ce SQL de créer une table temporaire et de verrouiller la table cible, puis de réexécuter le SQL. Voici les commandes d'opération et les résultats d'exécution pertinents :

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ;
Query OK, 1000000 rows affected (4.93 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

mysql> show create table test.sbtest1;
+---------+--------------------------+
| Table   | Create Table             |
+---------+--------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `k` int(11) NOT NULL DEFAULT '0',
  `c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
  `pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.4 Analyse comparative

Modification de la longueur des caractères UTF8(MB3) UTF8MB4
20->50 ddl en ligne (en place) ddl en ligne (en place)
50->100 ddl en ligne (copie) ddl en ligne (copie)
X->Y Quand Y*3<256, en place <br> Quand X*3>=256, en place Quand Y*4<256, en place <br> Quand X*4>=256, en place
Remarque Un caractère occupe au maximum 3 octets Un caractère occupe au maximum 4 octets

en conclusion

Lorsque la longueur maximale en octets d'un champ est >= 256 caractères, 2 octets sont nécessaires pour représenter la longueur du champ.

Exemple utilisant UTF8MB4 :

  • Pour que la longueur maximale en octets du champ varie dans les 256 caractères (c'est-à-dire x*4<256 et Y*4<256), ddl en ligne utilise le mode inplace, qui est très efficace.
  • Pour les champs dont la longueur maximale en octets varie au-delà de 256 caractères (c'est-à-dire x*4>=256 et Y*4>=256), le ddl en ligne utilise le mode inplace, qui est très efficace.
  • Sinon, le ddl en ligne utilise le mode copie, ce qui est inefficace.
  • Il en va de même pour UTF8 (MB3).

suggestion

Afin d'éviter une extension ultérieure de la longueur des champs, online ddl adopte le mode de copie inefficace. Il est recommandé que :

  • Pour le type de caractère UTF8(MB3) :
    • Le nombre de caractères est inférieur à 50. Il est recommandé de définir la longueur des caractères sur VARCHAR(50) ou moins.
    • Le nombre de caractères est proche de 84 (256/3=83,33). Il est recommandé de le définir sur varchar(84) ou sur une longueur de caractères plus grande.
  • Pour le type de caractère UTF8MB4 :
    • Si le nombre de caractères est inférieur à 50, il est recommandé de le définir sur VARCHAR(50), ou sur une longueur de caractères plus petite.
    • Le nombre de caractères est proche de 64 (256/4=64). Il est recommandé de le définir sur VARCHAR(64) ou sur une longueur de caractères plus grande.

Les résultats de cette vérification sont uniquement à titre de référence. Si vous devez opérer dans un environnement de production, veuillez définir raisonnablement la durée de VARCHAR en fonction de la situation réelle pour éviter des pertes économiques.

Pour des articles plus techniques, veuillez visiter : https://opensource.actionsky.com/

À propos de SQLE

SQLE est une plateforme complète de gestion de la qualité SQL qui couvre l'audit et la gestion SQL, du développement aux environnements de production. Il prend en charge les bases de données open source, commerciales et nationales grand public, fournit des capacités d'automatisation des processus pour le développement, l'exploitation et la maintenance, améliore l'efficacité en ligne et améliore la qualité des données.

SQLE obtenir

taper adresse
Dépôt https://github.com/actiontech/sqle
document https://actiontech.github.io/sqle-docs/
publier des nouvelles https://github.com/actiontech/sqle/releases
Documentation de développement du plug-in d'audit des données https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
Un programmeur né dans les années 1990 a développé un logiciel de portage vidéo et en a réalisé plus de 7 millions en moins d'un an. La fin a été très éprouvante ! Des lycéens créent leur propre langage de programmation open source en guise de cérémonie de passage à l'âge adulte - commentaires acerbes des internautes : s'appuyant sur RustDesk en raison d'une fraude généralisée, le service domestique Taobao (taobao.com) a suspendu ses services domestiques et repris le travail d'optimisation de la version Web Java 17 est la version Java LTS la plus utilisée Part de marché de Windows 10 Atteignant 70 %, Windows 11 continue de décliner Open Source Daily | Google soutient Hongmeng pour prendre le relais des téléphones Android open source pris en charge par Docker ; Electric ferme la plate-forme ouverte Apple lance la puce M4 Google supprime le noyau universel Android (ACK) Prise en charge de l'architecture RISC-V Yunfeng a démissionné d'Alibaba et prévoit de produire des jeux indépendants sur la plate-forme Windows à l'avenir
{{o.name}}
{{m.nom}}

Je suppose que tu aimes

Origine my.oschina.net/actiontechoss/blog/11094958
conseillé
Classement