Accede a todo EDteam con un único pago¡Sube a premium!

En MySQL nunca uses "utf8", usa "utf8mb4"

            Hola gente del futuro. En esta ocasión les traigo un artículo de Adam Hooper que me sacudió la mente, tanto así que decidí traducirlo para ustedes.

Diseño web
1 minuto
Hace 7 años
En MySQL nunca uses "utf8", usa "utf8mb4"

Hola gente del futuro.

En esta ocasión les traigo un artículo de Adam Hooper que me sacudió la mente, tanto así que decidí traducirlo para ustedes.

En MySQL nunca uses "utf8", usa "utf8mb4"

Bug de hoy: Traté de almacenar una cadena UTF-8 en una base de datos codificada con "utf8" de MariaDB, y Rails arroja un error extraño:

1Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘summary’ at row 1

Este es un cliente UTF-8 y un servidor UTF-8, en una base de datos UTF-8 con una intercalación UTF-8. La cadena, "? <...", es UTF-8 válido.

Pero aquí está el problema: MySQL "utf8" no es UTF-8.

La codificación "utf8" sólo admite tres bytes por carácter. La codificación UTF-8 real - que todo el mundo utiliza, incluido usted - necesita hasta cuatro bytes por carácter.

Los desarrolladores de MySQL nunca arreglaron este error. Lanzaron una solución en 2010: un nuevo conjunto de caracteres llamado "utf8mb4".

Por supuesto, nunca anunciaron esto (probablemente porque el error es muy embarazoso). Ahora, las guías en la Web sugieren que los usuarios usen "utf8". Todas esas guías están equivocadas.

En resumen:

  • MySQL "utf8mb4" significa "UTF-8".
  • MySQL "utf8" significa "una codificación de caracteres propietarios". Esta codificación no puede codificar muchos caracteres Unicode.

Voy a hacer una declaración dramática aquí: todos los usuarios de MySQL y MariaDB que están utilizando actualmente "utf8" deberían utilizar "utf8mb4". Nadie debería usar "utf8".

¿Qué es la codificación? ¿Qué es UTF-8?

Joel on Software escribió mi introducción favorita. La abreviaré.

Computadores de cualquier tipo almacenan texto como unos y ceros. La primera letra en este párrafo fue almacenada como "01000011" y tu computador dibujó "C". Tu computador eligió "C" en dos pasos:

  1. Tu computador leyó "01000011" y determinó que es el número 67. Eso es porque 67 fue codificado como "01000011".
  2. Tu computador buscó el número de caracteres 67 en el conjunto de caracteres Unicode, y encontró que 67 significa "C".

Lo mismo ocurrió cuando escribí ese "C":

  1. Mi computador asignó "C" a 67 en el conjunto de caracteres Unicode.
  2. Mi computador codificó 67, enviando "01000011" a este servidor web.

Los conjuntos de caracteres son un problema resuelto. Casi todos los programas en Internet utilizan el conjunto de caracteres Unicode, ya que no hay incentivo para usar otro.

Pero la codificación es más un llamado a fallas. Unicode tiene espacios para más de un millón de caracteres. ("C" y "?" son dos de estos caracteres). La codificación más simple, UTF-32, hace que cada carácter tome 32 bits. Eso es simple, porque los computadores han estado tratando grupos de 32 bits como números por siglos, y son realmente buenos en ello. Pero no es útil: es una pérdida de espacio.

UTF-8 ahorra espacio. En UTF-8, caracteres comunes como "C" toman 8 bits, mientras que caracteres raros como "?" toman 32 bits. Otros caracteres toman 16 o 24 bits. Una entrada de blog como esta toma aproximadamente cuatro veces menos espacio en UTF-8 que en UTF-32. Así que se carga cuatro veces más rápido.

Puede que no te des cuenta, pero nuestros computadores entendieron en UTF-8 detrás de las escenas. Si no lo hicieron, entonces cuando escribo "?" usted verá un desorden de datos aleatorios.

El conjunto de caracteres "utf8" de MySQL no coincide con otros programas. Cuando le dices "?", se opone.

Un poco de historia de MySQL

¿Por qué los desarrolladores de MySQL hicieron "utf8" inválido? Podemos adivinar mirando registros de confirmación.

MySQL soporta UTF-8 desde la versión 4.1. Eso fue en 2003 - antes de la norma UTF-8 de hoy, RFC 3629.

El estándar UTF-8 anterior, RFC 2279, soportaba hasta seis bytes por carácter. Los desarrolladores de MySQL codificaron RFC 2279 en la primera versión pre-pre-release de MySQL 4.1 el 28 de marzo de 2002.

Luego, en septiembre, se produjo un cambio críptico de un byte al código fuente de MySQL: "UTF8 ahora funciona con secuencias de hasta 3 bytes solamente".

¿Quién confirmó esto? ¿Por qué? No puedo decirlo. El repositorio de código de MySQL parece haber perdido nombres de autores antiguos cuando adoptó Git. (MySQL usaba BitKeeper, como el kernel de Linux). No hay nada en la lista de correo alrededor de septiembre de 2003 que explique el cambio.

Pero puedo adivinar.

En 2002, MySQL dio a los usuarios un aumento de velocidad si los usuarios pudieran garantizar que cada fila de una tabla tenía el mismo número de bytes. Para ello, los usuarios declararían columnas de texto como "CHAR". Una columna "CHAR" siempre tiene el mismo número de caracteres. Si se le dan pocos caracteres, añade espacios al final; Si usted le da demasiados caracteres, trunca los últimos.

Cuando los desarrolladores de MySQL probaron por primera vez UTF-8, con su back-in-the-day seis bytes por carácter, probablemente se frustraron: una columna CHAR (1) tomaría seis bytes; Una columna CHAR (2) tomaría 12 bytes; y así.

Seamos claros: ese comportamiento inicial, que nunca fue lanzado, era correcto. Estaba bien documentado y ampliamente adoptado, y cualquiera que entendiera la UTF-8 estaría de acuerdo en que era correcto.

Pero claramente, un desarrollador de MySQL (o empresario) estaba preocupado de que un usuario o dos hicieran dos cosas:

  1. Elegir las columnas CHAR. (El formato CHAR es una reliquia hoy en día, pero en ese tiempo MySQL era más rápido con las columnas CHAR. Desde 2005, no lo es).
  2. Elegir para codificar esas columnas CHAR como "UTF-8".

Mi conjetura es que los desarrolladores de MySQL rompieron su codificación "utf8" para ayudar a estos usuarios: los usuarios que tanto 1) trataron de optimizar el espacio y la velocidad; y 2) fallaron en optimizar la velocidad y el espacio.

Nadie ganó. Los usuarios que querían velocidad y espacio todavía estaban equivocados al usar columnas CHAR "utf8", porque esas columnas eran aún más grandes y más lentas de lo que tenían que ser. Y los desarrolladores que querían exactitud estaban equivocados al usar "utf8", porque no puede almacenar "?".

Una vez que MySQL publicó este conjunto de caracteres no válido, nunca podría arreglarlo: eso obligaría a cada usuario a reconstruir cada base de datos. MySQL finalmente lanzó soporte UTF-8 en 2010, con un nombre diferente: "utf8mb4".

¿Por qué es tan frustrante?

Claramente me sentí frustrado esta semana. Mi error era difícil de encontrar porque me engañó con el nombre "utf8". Y no soy el único - casi todos los artículos que encontré en línea promocionan "utf8", así, UTF-8.

El nombre "utf8" siempre fue un error. Es un juego de caracteres patentado. Creó nuevos problemas y no resolvió el problema que pretendía resolver.

Es publicidad falsa.

Mis lecciones para llevar

  1. Los sistemas de base de datos tienen bugs sutiles y rarezas, y se pueden evitar muchos errores evitando los sistemas de bases de datos.
  2. Si necesita una base de datos, no utilice MySQL o MariaDB. Utilice PostgreSQL.
  3. Si necesitas usar MySQL o MariaDB, nunca use "utf8". Siempre use "utf8mb4" cuando quiera UTF-8. Convierte tu base de datos ahora para evitar dolores de cabeza más tarde.

El artículo original está en inglés, puedes verlo aquí: In MySQL, never use "utf8". Use "utf8mb4"

Eso es todo por ahora gente del futuro, nos leemos en el siguiente artículo.

Comentarios de los usuarios

Pregunta a ChatEDT