Mejores prácticas de evaluación de longitud de MySQL VARCHAR

¿Su VARCHAR tiene la longitud correcta?

Autor: Guan Yongqiang, miembro del equipo de DBA de Aikesheng, tiene buenas habilidades de operación y mantenimiento de MySQL. Le encanta aprender nuevos conocimientos y también es un otaku al que le encanta jugar.

Autor: Li Fuqiang, miembro del equipo de Aikesheng DBA, familiarizado con MySQL, TiDB, OceanBase y otras bases de datos. Creo que si continúas haciendo bien las cosas correctas, obtendrás diferentes recompensas.

Producido por la comunidad de código abierto de Aikeson. El contenido original no se puede utilizar sin autorización. Comuníquese con el editor e indique la fuente para la reimpresión.

Este artículo tiene aproximadamente 2200 palabras y se espera que su lectura demore 8 minutos.

Descripción de fondo

Algunos clientes informaron que ampliaron la longitud de un campo de tipo VARCHAR . La primera vez puedes modificarlo rápidamente, pero la segunda vez tarda mucho en ejecutarse. Estoy confundido porque la cantidad de datos en la tabla es casi la misma. ¿Por qué es más rápido VARCHAR(20)ajustar de a , pero lleva mucho tiempo ajustar de a ?VARCHAR(50)VARCHAR(50)VARCHAR(100) Entonces reproducimos la situación y realizamos un análisis del problema.

información ambiental

Los productos y la información de versión involucrados en esta verificación son los siguientes:

producto Versión
mysql 5.7.Servidor comunitario MySQL de 25 registros (GPL)
Banco del sistema banco de sistemas 1.0.17

Recurrencia de escena

3.1 Preparación de datos

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 Verificación de problemas

Para simular la descripción del cliente, cmodificamos el campo, VARCHAR(20)lo modificamos VARCHAR(50)y luego lo modificamos VARCHAR(100)y observamos el tiempo requerido para su ejecución. Los siguientes son los comandos de operación relevantes y los resultados de ejecución:

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)

A través de la verificación, se descubrió que el problema reaparecería de manera estable, por lo que continuamos intentando modificarlo. Finalmente, descubrimos que tomó mucho tiempo modificarlo VARCHAR(63), VARCHAR(64)pero después de 64, continuamos expandiendo la longitud y descubrimos que. podría completarse rápidamente.

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 Análisis de problemas

Analice la situación en la que lleva mucho tiempo VARCHAR(63)modificarla . VARCHAR(64)Al consultar la documentación oficial , descubrimos que VARCHARel tipo de carácter que puede almacenar caracteres cuando la longitud del byte es 1 es 0 ~ 255. El tipo de juego de caracteres actual es UTF8MB4. Dado que UTF8MB4 es un juego de caracteres codificado de cuatro bytes, es decir, una longitud de byte puede almacenar 63,75 (255/4) caracteres, por lo que cuando lo modificamos aVARCHAR(63) , debemos agregar un byte para los datos. VARCHAR(64)Para el procesamiento, es necesario completar esta expansión de longitud estableciendo una tabla temporal, por lo que lleva mucho tiempo.

Verificación extendida

4.1 Preparación de datos

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 Verificación de escena UTF8

Dado que UTF8 es un conjunto de caracteres de codificación de tres bytes, un byte puede almacenar 85 (255/3 = 85) caracteres.

La secuencia de esta modificación es: VARCHAR(20)→VARCHAR(50)→VARCHAR(85), y observe el tiempo requerido para su ejecución. Los siguientes son los comandos de operación relevantes y los resultados de la ejecución:

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)

Secuencia de modificación: VARCHAR (85) → VARCHAR (86) → VARCHAR (100). En este momento, observaremos que la declaración SQL ejecutada devuelve directamente un error. Entonces eliminamos algorithm=inplace ,lock=noneestos dos parámetros, lo que permite que este SQL cree una tabla temporal y bloquee la tabla de destino, y luego vuelva a ejecutar el SQL. Los siguientes son los comandos de operación relevantes y los resultados de la ejecución:

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 Verificación del escenario UTF8MB4

Dado que UTF8MB4 es un conjunto de caracteres de codificación de cuatro bytes, un byte puede almacenar 63 (255/4 = 63,75) caracteres.

La secuencia de esta modificación es: VARCHAR(20)→VARCHAR(50)→VARCHAR(63), y observe el tiempo requerido para su ejecución. Los siguientes son los comandos de operación relevantes y los resultados de la ejecución:

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 secuencia de esta modificación es: VARCHAR (63) → VARCHAR (64) → VARCHAR (100). En este momento, observaremos que la declaración SQL ejecutada devuelve directamente un error. Entonces eliminamos algorithm=inplace, lock=noneestos dos parámetros, lo que permite que este SQL cree una tabla temporal y bloquee la tabla de destino, y luego vuelva a ejecutar el SQL. Los siguientes son los comandos de operación relevantes y los resultados de la ejecución:

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 Análisis comparativo

Modificación de la longitud de los caracteres UTF8 (MB3) UTF8MB4
20->50 ddl en línea (in situ) ddl en línea (in situ)
50->100 ddl en línea (copia) ddl en línea (copia)
X->Y Cuando Y*3<256, en el lugar <br> Cuando X*3>=256, en el lugar Cuando Y*4<256, en el lugar <br> Cuando X*4>=256, en el lugar
Observación Un carácter ocupa un máximo de 3 bytes Un carácter ocupa un máximo de 4 bytes

en conclusión

Cuando la longitud máxima en bytes de un campo es >= 256 caracteres, se necesitan 2 bytes para representar la longitud del campo.

Ejemplo usando UTF8MB4:

  • Para la longitud máxima de bytes del campo que varía dentro de los 256 caracteres (es decir, x*4<256 e Y*4<256), el ddl en línea utiliza el modo in situ, que es muy eficiente.
  • Para los campos cuya longitud máxima en bytes varía más allá de los 256 caracteres (es decir, x*4>=256 e Y*4>=256), el ddl en línea utiliza el modo in situ, que es muy eficiente.
  • De lo contrario, el ddl en línea utiliza el modo de copia, lo cual es ineficaz.
  • Lo mismo ocurre con UTF8 (MB3).

sugerencia

Para evitar una expansión posterior de la longitud del campo, ddl en línea adopta el modo de copia ineficiente. Se recomienda:

  • Para el tipo de carácter UTF8(MB3):
    • El número de caracteres es inferior a 50. Se recomienda establecer la longitud de los caracteres en VARCHAR(50) o menos.
    • El número de caracteres es cercano a 84 (256/3 = 83,33). Se recomienda configurarlo en varchar (84) o una longitud de caracteres mayor.
  • Para el tipo de carácter UTF8MB4:
    • Si el número de caracteres es inferior a 50, se recomienda establecerlo en VARCHAR(50), o una longitud de caracteres menor.
    • El número de caracteres es cercano a 64 (256/4 = 64). Se recomienda configurarlo en VARCHAR (64) o una longitud de caracteres mayor.

Los resultados de esta verificación son solo como referencia. Si necesita operar en un entorno de producción, defina razonablemente la longitud de VARCHAR en función de la situación real para evitar pérdidas económicas.

Para obtener más artículos técnicos, visite: https://opensource.actionsky.com/

Acerca de SQLE

SQLE es una plataforma integral de gestión de calidad de SQL que cubre la auditoría y gestión de SQL desde los entornos de desarrollo hasta los de producción. Admite bases de datos nacionales, comerciales y de código abierto convencionales, proporciona capacidades de automatización de procesos para el desarrollo, operación y mantenimiento, mejora la eficiencia en línea y mejora la calidad de los datos.

obtener SQLE

tipo DIRECCIÓN
Repositorio https://github.com/actiontech/sqle
documento https://actiontech.github.io/sqle-docs/
noticias de lanzamiento https://github.com/actiontech/sqle/releases
Documentación de desarrollo del complemento de auditoría de datos https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
Un programador nacido en los años 90 desarrolló un software de portabilidad de vídeo y ganó más de 7 millones en menos de un año. ¡El final fue muy duro! Los estudiantes de secundaria crean su propio lenguaje de programación de código abierto como una ceremonia de mayoría de edad: comentarios agudos de los internautas: debido al fraude desenfrenado, confiando en RustDesk, el servicio doméstico Taobao (taobao.com) suspendió los servicios domésticos y reinició el trabajo de optimización de la versión web Java 17 es la versión Java LTS más utilizada. Cuota de mercado de Windows 10. Alcanzando el 70%, Windows 11 continúa disminuyendo. Open Source Daily | Google apoya a Hongmeng para hacerse cargo de los teléfonos Android de código abierto respaldados por Docker; Electric cierra la plataforma abierta Apple lanza el chip M4 Google elimina el kernel universal de Android (ACK) Soporte para la arquitectura RISC-V Yunfeng renunció a Alibaba y planea producir juegos independientes en la plataforma Windows en el futuro
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/actiontechoss/blog/11094958
Recomendado
Clasificación