¿Cuál es el impacto en el rendimiento del uso de CHAR vs VARCHAR en un campo de tamaño fijo?


58

Tengo una columna indexada que almacena un hash MD5. Por lo tanto, la columna siempre almacenará un valor de 32 caracteres. Por alguna razón, esto fue creado como un varchar en lugar de un char. ¿Merece la pena migrar la base de datos para convertirla en un char? Esto está en MySQL 5.0 con InnoDB.


66
ADVERTENCIA Esta pregunta y sus respuestas fueron escritas antes de InnoDB y utf8 eran los valores predeterminados.
Rick James

Respuestas:


56

Se hizo una pregunta similar antes

Implicaciones de rendimiento de los tamaños de MySQL VARCHAR

Aquí está el extracto de mi respuesta.

Debe darse cuenta de las ventajas de usar CHAR vs VARCHAR

Con los campos CHAR, lo que asigna es exactamente lo que obtiene. Por ejemplo, CHAR (15) asigna y almacena 15 bytes, sin importar la cantidad de caracteres que coloque en el campo. La manipulación de cadenas es simple y directa ya que el tamaño del campo de datos es totalmente predecible.

Con los campos VARCHAR, obtienes una historia completamente diferente. Por ejemplo, VARCHAR (15) en realidad asigna dinámicamente hasta 16 bytes, hasta 15 para datos y, al menos, 1 byte adicional para almacenar la longitud de los datos. Si tiene la cadena 'hola' para almacenar que tomará 6 bytes, no 5. La manipulación de cadenas siempre debe realizar alguna forma de verificación de longitud en todos los casos.

La compensación es más evidente cuando haces dos cosas: 1. Almacenamiento de millones o miles de millones de filas 2. Columnas de indexación que son CHAR o VARCHAR

TRADEOFF # 1 Obviamente, VARCHAR tiene la ventaja ya que los datos de longitud variable producirían filas más pequeñas y, por lo tanto, archivos físicos más pequeños.

TRADEOFF # 2 Dado que los campos CHAR requieren menos manipulación de cadenas debido a los anchos de campo fijos, las búsquedas de índice contra el campo CHAR son en promedio un 20% más rápidas que las de los campos VARCHAR. Esta no es ninguna conjetura de mi parte. El libro MySQL Database Design and Tuning realizó algo maravilloso en una tabla MyISAM para probar esto. El ejemplo en el libro hizo algo como lo siguiente:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Esta directiva obliga a todos los VARCHAR a comportarse como CHAR. Hice esto en mi trabajo anterior en 2007 y tomé una tabla de 300GB y aceleré las búsquedas de índice en un 20%, sin cambiar nada más. Funcionó según lo publicado. Sin embargo, produjo una tabla de casi el doble de tamaño, pero eso simplemente se remonta a la compensación # 1.

Puede analizar los datos que se almacenan para ver qué recomienda MySQL para la definición de columnas. Simplemente ejecute lo siguiente en cualquier tabla:

SELECT * FROM tblname PROCEDURE ANALYSE();

Esto atravesará toda la tabla y recomendará definiciones de columna para cada columna en función de los datos que contiene, los valores mínimos de campo, los valores máximos de campo, etc. A veces, solo tiene que usar el sentido común al planificar CHAR vs VARCHAR. Aquí hay un buen ejemplo:

Si está almacenando direcciones IP, la máscara para dicha columna tiene como máximo 15 caracteres (xxx.xxx.xxx.xxx). Me gustaría saltar CHAR(15)en un abrir y cerrar de ojos porque las longitudes de las direcciones IP no variarán mucho y la complejidad adicional de la manipulación de cadenas controlada por un byte adicional. Todavía podrías hacer una PROCEDURE ANALYSE()contra tal columna. Incluso puede recomendar VARCHAR. Mi dinero todavía estaría en CHAR sobre VARCHAR en este caso.

Los problemas de CHAR vs VARCHAR solo se pueden resolver mediante una planificación adecuada. Con un gran poder viene una gran responsabilidad (cliché pero cierto).

ACTUALIZAR

Cuando se trata de MD5, el cálculo de strleninternamente debe eliminarse al cambiar todo el formato de fila. No habría necesidad de cambiar la definición del campo.

Si la clave MD5 es el único VARCHAR presente, lo buscaría y convertiría el formato de fila de la tabla a fijo . Si hay un número significativo de otros campos VARCHAR presentes, también se beneficiarían. A cambio, la tabla se expandiría a aproximadamente el doble de su tamaño. Pero las consultas deberían acelerar un 20% más sin ajustes adicionales.


1
Creo que usaría un char (4) o algo así como un entero sin signo para una dirección IP
Jack Douglas

@JackPDouglas Tienes razón en ese punto.
RolandoMySQLDBA

¿No se almacenan los índices con una longitud fija de todos modos? No entiendo cómo cambiar el formato de almacenamiento a búsquedas de índice mejorado de longitud fija. ¿Quieres decir que mejoró los escaneos de tablas?
Marcus Adams

1
@JackDouglas, ¿por qué no bity binary?
Pacerier

@Pacerier eso sería mejor, estoy de acuerdo :)
Jack Douglas

19

Parece que ahorrará 1 byte por valor o alrededor del 3% al convertir a char. Probablemente no valga la pena si está almacenando MD5 en hexadecimal de todos modos; podría ahorrar un 50% utilizando un binaryen su lugar.

Gracias a Ovais (ver comentarios) por señalar que char(32)puede usar mucho más de 32 bytes si está usando un juego de caracteres multibyte.

Gracias a Rick James por señalar que debería usar la unhexfunción para convertir una cadena hexadecimal a binario:

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;
El | longitud (barra) |
El | ----------: |
El | 32
El | 16

db <> violín aquí


Buen llamado para cambiar a binario.
RThomas

Estoy planeando convertir esto en un binario. Sin embargo, ahora que lo pienso, el tamaño no debería ser diferente solo en función de si estoy usando un byte o un carácter ya que nuestra codificación es utf-8. ¿O estoy equivocado?
Jason Baker

@ Jason: la codificación no se aplica a binary, ¿o he entendido mal?
Jack Douglas

3
para una columna char (32) con un conjunto de caracteres de utf-8, cada valor necesitaría 32x3 bytes para el almacenamiento. ¿Por qué necesitaría establecer el valor hash MD5 para ser utf-8? La conversión a binario (32) necesitaría 32 bytes por valor.
ovais.tariq

1
Cambiar a BINARYhace muy poco a menos que también lo use UNHEX(). Es decir, se puede almacenar UNHEX(MD5(x))en un 16 bytes BINARY(16)para ahorrar espacio significativo durante el almacenamiento MD5(x)en CHAR(32) CHARACTER SET ascii.
Rick James

15

No vale la pena cambiar en mi opinión. Si mira la documentación aquí, debería ilustrar la diferencia entre los dos. En su escenario de uso, uno realmente no ofrece ningún beneficio significativo sobre el otro a menos que esté realmente preocupado por la carga adicional relacionada con el tamaño de la fila.

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

También tenga en cuenta el primer comentario sobre la documentación que enlace arriba ... "CHAR solo acelerará su acceso si todo el registro es de tamaño fijo. Es decir, si usa cualquier objeto de tamaño variable, también podría hacerlos todos tamaño variable. No gana velocidad utilizando un CHAR en una tabla que también contiene un VARCHAR "


Esa "aceleración" se aplica a MyISAM, no a InnoDB.
Rick James
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.