MySQL: ¿VARCHAR grande vs. TEXTO?

847

Tengo una tabla de mensajes en MySQL que registra los mensajes entre usuarios. Además de los identificadores y tipos de mensaje típicos (todos los tipos enteros), necesito guardar el texto del mensaje real como VARCHAR o TEXT. Estoy estableciendo un límite frontal de 3000 caracteres, lo que significa que los mensajes nunca se insertarán en la base de datos más tiempo que esto.

¿Existe una justificación para utilizar VARCHAR (3000) o TEXT? Hay algo acerca de solo escribir VARCHAR (3000) que se siente algo contra-intuitivo. He leído otras publicaciones similares en Stack Overflow, pero sería bueno obtener vistas específicas para este tipo de almacenamiento de mensajes comunes.

Tom
fuente
28
Un poco viejo, pero vine aquí porque me encontré con un problema que me hizo pensar en esto. En mi caso, mi formulario de front-end se limitó a 2,000 caracteres, pero la codificación implícita en mi método de almacenamiento codificó caracteres internacionales como caracteres múltiples (que aparentemente pueden ser de 3 a 12 por carácter). Entonces mis 2,000 de repente se convierten en 24,000. Algo para pensar ...
James S
3
He encontrado que el texto es significativamente más rápido para muchas inserciones concurrentes.
Ray S.
1
@JamesS: utf8mb4 ...>. <
indivisible el
10
@RickJames considera publicar una respuesta actualizada, en lugar de cerrar la pregunta
Yvette
3
@YvetteColomb: agregué una respuesta. Principalmente me gustaría deshacerme de la respuesta aceptada porque está desactualizada . Llegué a las preguntas y respuestas porque alguien citaba información incorrecta y decía "754 votos a favor, así que debe ser correcto". OK, también edité la respuesta aprobada. (Aunque eso se siente inapropiado.)
Rick James

Respuestas:

812
  • TEXTy BLOB puede almacenarse fuera de la tabla con la tabla simplemente con un puntero a la ubicación del almacenamiento real. El lugar donde se almacena depende de muchas cosas, como el tamaño de los datos, el tamaño de las columnas, el formato de fila y la versión de MySQL.

  • VARCHARse almacena en línea con la tabla. VARCHARes más rápido cuando el tamaño es razonable, cuya compensación sería más rápida depende de sus datos y su hardware, querría comparar un escenario del mundo real con sus datos.

MindStalker
fuente
149
+1: VARCHAR (almacenado en línea) suele ser más rápido SI los datos se recuperan con frecuencia (incluidos en la mayoría de las consultas). Sin embargo, para un gran volumen de datos que normalmente no se recuperan (es decir, que ninguna consulta hace referencia a ellos), puede ser mejor no tener los datos almacenados en línea. Hay un límite superior en el tamaño de la fila, para los datos almacenados en línea.
spencer7593
22
@Pacerier: el beneficio exacto de evitar el almacenamiento "en línea" es un aumento en el número de filas que se pueden almacenar en un bloque, lo que significa que las filas de la tabla ocupan menos bloques en el caché del búfer InnoDB (menor huella de memoria) y significa menos bloques para transferir desde y hacia el disco (E / S reducida). Pero, esto es solo un beneficio de rendimiento si las columnas almacenadas "fuera de fila" en gran medida no están referenciadas por consultas. Si la mayoría de las consultas hacen referencia a esas columnas "fuera de fila", ese beneficio se evapora en gran medida. Se prefiere en línea si las columnas se ajustan al tamaño máximo de filas y se hace referencia con frecuencia.
spencer7593
232
"VARCHAR es más rápido cuando el tamaño es razonable". ¿Qué es un número "razonable" de caracteres, 100? 1000? 100.000?
Tim Peterson
126
Esta respuesta no es correcta para InnoDB. Tanto VARCHAR como BLOB / TEXT se almacenan en línea con otras columnas si el valor de una fila dada se ajusta al tamaño de la página (16 KB y cada página debe contener al menos dos filas). Si la cadena es demasiado grande para eso, se desborda a páginas adicionales. Consulte mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb para obtener una explicación detallada.
Bill Karwin el
15
@BillKarwin ... Si lo entiendo correctamente, ¿no debería haber una diferencia de rendimiento entre varchary blob/ texten InnoDB para elementos de texto pequeños? Por lo tanto ¿sería prudente simplemente hacer todo varcharun texttipo y dejar que la base de datos a manejar el desbordamiento vs línea?
ryvantage
475

¿Puedes predecir cuánto tiempo duraría la entrada del usuario?

VARCHAR (X)

Caso: nombre de usuario, correo electrónico, país, asunto, contraseña


TEXTO

Caso: mensajes, correos electrónicos, comentarios, texto formateado, html, código, imágenes, enlaces


TEXTO MEDIO

Caso: grandes cuerpos json, libros cortos a medianos, cadenas csv


TEXTO LARGO

Caso: libros de texto, programas, años de archivos de registros, Harry Potter y el cáliz de fuego, registro de investigaciones científicas

Michael J. Calkins
fuente
8
La previsibilidad es realmente un elemento secundario aquí. En realidad, la longitud máxima esperada debería ser el factor decisivo. Los elementos que menciona como más predecibles son solo así porque son más cortos que los demás.
Andrew Barber
30
@ Andrew-barber Sin embargo, ese es mi punto. Todas las otras publicaciones explican bien sobre las diferencias, pero no sobre las situaciones en las que realmente tiene que elegir entre las dos. Estaba tratando de señalar que usar varchar para predeciblemente corto es una buena opción y usar texto para arbitrariamente largo es una buena opción.
Michael J. Calkins el
1
Si todas las columnas son cortas y predecibles (por ejemplo, la dirección MAC, IMEI, etc. son cosas que nunca cambian), use columnas CHAR y puede hacer que el tamaño de su fila sea fijo, lo que debería acelerar las cosas considerablemente si usa MyISAM, posiblemente También InnoDb aunque no estoy seguro de eso.
Matt
1
@ MichaelJ.Calkins Cosa que sucedió en MySQL 5.6. Ahora también tiene búsqueda de texto completo en InnoDB. Ver dev.mysql.com/doc/refman/5.6/en/fulltext-search.html
PhoneixS
77
Límites de caracteres: TINYTEXT: 255; TEXTO: 65.535; MEDIO TEXTO: 16.777.215; LONGTEXT: 4,294,967,29.
Victor Stoddard
219

Solo para aclarar la mejor práctica:

  1. Los mensajes de formato de texto casi siempre deben almacenarse como TEXTO (terminan siendo arbitrariamente largos)

  2. Los atributos de cadena deben almacenarse como VARCHAR (el nombre de usuario de destino, el asunto, etc.).

Entiendo que tienes un límite frontal, lo cual es genial hasta que no lo sea. * sonrisa * El truco es pensar en el DB como algo separado de las aplicaciones que se conectan a él. El hecho de que una aplicación ponga un límite a los datos, no significa que los datos estén intrínsecamente limitados.

¿Qué tienen los mensajes mismos que los obliga a nunca tener más de 3000 caracteres? Si es solo una restricción de aplicación arbitraria (por ejemplo, para un cuadro de texto o algo así), use un TEXTcampo en la capa de datos.

James
fuente
¿Qué significa "que es genial hasta que no lo es"? ¿A qué se refiere "no es"?
Pacerier
77
@Pacerier Para darle un ejemplo del "no es" probable que James esté hablando sobre: ​​Tomemos como ejemplo Twitter, que hasta hace muy poco tenía un límite de 140 caracteres en los PM. Decidieron que ya no era sensato y decidieron eliminar ese límite por completo. Si no hubieran pensado en eso (lo cual estoy bastante seguro de que probablemente hicieron ...) se habrían encontrado con el escenario descrito anteriormente.
PaulSkinner
99
Solo estoy colocando nuestra nueva base de datos, y asumí que nadie podría poner más de 2000 caracteres en nuestros pequeños cuadros de comentarios, y luego, como señala James, esta noche de repente "no estaba bien" porque un usuario ingresó comentario muy válido que tenía 2600 caracteres de largo. Había usado varchar (2000) pensando que no podría ser más largo que eso, y me equivoqué. así que sí, es genial hasta que no lo sea. En nuestro caso, eso tardó solo unos días en manifestarse. La siguiente regla, Michael J. Calkins, creo que la usaré de ahora en adelante. Texto para mensajes, comentarios.
Lizardx
1
@Pacerier "que es genial hasta que no lo sea". En otras palabras, funciona casi todo el tiempo y es maravilloso ... excepto aquellas situaciones excepcionales en las que no es tan bueno.
Expiación limitada
@Pacerier se menciona otro ejemplo interesante en los comentarios de la respuesta seleccionada, básicamente tenía un límite frontal de 2,000 caracteres, pero los caracteres introducidos estaban en una página de códigos que en realidad usaba más bytes que letras normales, su base de datos terminó necesitando espacio para 24k caracteres solo porque tenía que tener en cuenta el tamaño de byte real de los caracteres que se presentaban.
RaptorX
32

Descargo de responsabilidad: no soy un experto en MySQL ... pero esta es mi comprensión de los problemas.

Creo que TEXT se almacena fuera de la fila mysql, mientras que VARCHAR se almacena como parte de la fila. Hay una longitud máxima de fila para las filas mysql ... por lo que puede limitar la cantidad de otros datos que puede almacenar en una fila utilizando VARCHAR.

También debido a que VARCHAR forma parte de la fila, sospecho que las consultas que miran ese campo serán un poco más rápidas que las que usan un fragmento de TEXTO.

Michael Anderson
fuente
38
El límite de longitud de la fila es 65.535 bytes [ dev.mysql.com/doc/refman/5.0/en/column-count-limit.html ]. Si su columna tiene codificación utf8, eso significa que una varcharcolumna de 3000 caracteres puede ocupar hasta 9000 bytes.
Jan Fabry
77
Los caracteres UTF-8 pueden tener hasta 4 bytes, por lo que creo que quiso decir 12,000 bytes (a menos que haya algo de MySQL que no entiendo aquí).
raylu
13
El UTF-8 de @raylu MySQL es "UTF-8 falso" en el sentido de que solo admite 3 bytes por carácter máximo, por lo que no hay forma de almacenar directamente caracteres unicode más allá del plano BMP en el UTF-8 de MySQL. Esto se soluciona en MySQL 5.5.
Pacerier
2
Creo que esta afirmación es válida solo para MyISAM. No puedo encontrar una fuente definitiva, pero creo que InnoDB también almacena TEXTen línea en la tabla.
dotancohen
2
@dotancohen Encontré una fuente aquí que explica que el almacenamiento de datos de longitud variable usando InnoDB puede variar (puede almacenarse externamente o en línea dentro de la fila) mysqlserverteam.com/externally-stored-fields-in-innodb
KiX Ortillan
30

Respuesta corta: no hay diferencias prácticas, de rendimiento o de almacenamiento.

Respuesta larga:

Esencialmente no hay diferencia (en MySQL) entre VARCHAR(3000)(o cualquier otro límite grande) y TEXT. El primero se truncará a 3000 caracteres ; este último se truncará a 65535 bytes . (Hago una distinción entre bytes y caracteres porque un personaje puede tomar varios bytes).

Para límites más pequeños VARCHAR, hay algunas ventajas sobre TEXT.

  • "más pequeño" significa 191, 255, 512, 767 o 3072, etc., según la versión, el contexto y CHARACTER SET.
  • INDEXesestán limitados en qué tan grande se puede indexar una columna. (767 o 3072 bytes ; esto depende de la versión y la configuración)
  • Las tablas intermedias creadas por complex SELECTsse manejan de dos maneras diferentes: MEMORY (más rápido) o MyISAM (más lento). Cuando están involucradas columnas 'grandes', la técnica más lenta se selecciona automáticamente. (Cambios significativos en la versión 8.0; por lo tanto, este elemento de viñeta está sujeto a cambios).
  • En relación con el elemento anterior, todos los TEXTtipos de datos (en oposición a VARCHAR) saltan directamente a MyISAM. Es decir, TINYTEXTes automáticamente peor para las tablas temporales generadas que el equivalente VARCHAR. (¡Pero esto lleva la discusión en una tercera dirección!)
  • VARBINARYes como VARCHAR; BLOBes como TEXT.

Refutación a otras respuestas.

La pregunta original preguntaba una cosa (qué tipo de datos usar); la respuesta aceptada respondió algo más (almacenamiento no registrado). Esa respuesta ahora está desactualizada.

Cuando este hilo se inició y respondió, solo había dos "formatos de fila" en InnoDB. Poco después, se introdujeron dos formatos más ( DYNAMICy COMPRESSED).

La ubicación de almacenamiento para TEXTy VARCHAR()se basa en el tamaño , no en el nombre del tipo de datos . Para una discusión actualizada sobre el almacenamiento dentro / fuera del registro de grandes columnas de texto / blob, vea esto .

Rick James
fuente
1
Una buena idea aquí. Esta debería ser la respuesta aceptada.
Kosta Kontos
2
@KostaKontos: gracias por los elogios y la corrección de errores tipográficos. Cuando vea la necesidad de una mejor respuesta, agregaré una respuesta, incluso si 8 años y 800 votos positivos son demasiado tarde.
Rick James
7

Las respuestas anteriores no insisten lo suficiente en el problema principal: incluso en consultas muy simples como

(SELECT t2.* FROM t1, t2 WHERE t2.id = t1.id ORDER BY t1.id) 

Se puede requerir una tabla temporal, y si un VARCHARcampo está involucrado, se convierte en un CHARcampo en la tabla temporal. Entonces, si tiene en su tabla 500,000 líneas con un VARCHAR(65000)campo, esta columna solo usará 6.5 * 5 * 10 ^ 9 byte. Dichas tablas temporales no se pueden manejar en la memoria y se escriben en el disco. Se puede esperar que el impacto sea catastrófico.

Fuente (con métricas): https://nicj.net/mysql-text-vs-varchar-performance/ (Esto se refiere al manejo de TEXTvs VARCHARen el motor de almacenamiento MyISAM "estándar" (?). Puede ser diferente en otros, por ejemplo, InnoDB.)

Max
fuente
3
InnoDB: Lo mismo se aplica a través de la versión 5.7. Con 8.0, las temperaturas varchar son de longitud variable.
Rick James
3

Hay una GRAN diferencia entre VARCHAR y TEXT. Mientras que los campos VARCHAR pueden indexarse, los campos TEXT no pueden. Los campos de tipo VARCHAR se almacenan en línea mientras que TEXT se almacenan fuera de línea, solo los punteros a los datos de TEXT se almacenan realmente en los registros.

Si tiene que indexar su campo para buscar, actualizar o eliminar más rápidamente que ir a VARCHAR, no importa cuán grande sea. Un VARCHAR (10000000) nunca será lo mismo que un campo TEXTO porque estos dos tipos de datos son de naturaleza diferente.

  • Si usa su campo solo para archivar
  • no te importa la recuperación de la velocidad de datos
  • le importa la velocidad, pero usará el operador '% LIKE%' en su consulta de búsqueda, por lo que la indexación no ayudará mucho
  • no puedes predecir un límite de longitud de datos

que ir a TEXTO.

Viktor Joras
fuente
Información parcialmente engañosa: las columnas TEXT no pueden indexarse ​​en su totalidad. Cuando incluye una columna TEXTO en el índice, debe especificar la longitud. Además, los VARCHAR no se pueden indexar en su totalidad en el caso de VARCHAR> 255, ya que existe una longitud máxima en el tamaño del índice.
eRadical
2

Varchar es para datos pequeños como direcciones de correo electrónico, mientras que Text es para datos mucho más grandes como artículos de noticias, Blob para datos binarios como imágenes.

El rendimiento de Varchar es más potente porque se ejecuta completamente desde la memoria, pero este no será el caso si los datos son demasiado grandes como, varchar(4000)por ejemplo.

El texto, por otro lado, no se adhiere a la memoria y se ve afectado por el rendimiento del disco, pero puede evitarlo separando los datos de texto en una tabla separada y aplicando una consulta de unión izquierda para recuperar datos de texto.

Blob es mucho más lento, así que úselo solo si no tiene muchos datos como 10000 imágenes que costarán 10000 registros.

Siga estos consejos para obtener la máxima velocidad y rendimiento:

  1. Use varchar para nombre, títulos, correos electrónicos

  2. Usar texto para datos grandes

  3. Texto separado en diferentes tablas

  4. Use las consultas de combinación izquierda en una identificación, como un número de teléfono

  5. Si va a usar Blob, aplique los mismos consejos que en Text

Esto hará que las consultas cuesten milisegundos en tablas con datos> 10 M y un tamaño de hasta 10 GB garantizado.

Creativo87
fuente