¿Cuál es la diferencia entre utf8_general_ci y utf8_unicode_ci?

1063

Entre utf8_general_ciy utf8_unicode_ci, ¿hay alguna diferencia en términos de rendimiento?

KahWee Teng
fuente
1
Ver también stackoverflow.com/questions/1036454/…
hasta el
66
Si lo desea utf8[mb4]_unicode_ci, puede que le guste utf8[mb4]_unicode_520_ciaún más.
Rick James
8
No sé cómo me siento al respecto: en lugar de arreglar su implementación para seguir el último estándar Unicode, mantienen la versión obsoleta como predeterminada y la gente tiene que agregar "520" para usar la correcta ahora. Y no es compatible con versiones anteriores y posteriores porque no puede usar la versión "520" en versiones anteriores de MySQL. ¿Por qué no podrían haber actualizado su colación existente? Lo mismo con "mb4", de verdad. ¿Qué código realmente dependía del comportamiento antiguo, limitado / obsoleto para justificar mantenerlo como predeterminado?
thomasrutter
77
Aún mejor es el valor predeterminado de 8.0 utf8mb4_0900_ai_ci.
Rick James el

Respuestas:

1591

Estas dos intercalaciones son para la codificación de caracteres UTF-8. Las diferencias están en cómo se clasifica y compara el texto.

Nota: En MySQL debe usar en utf8mb4lugar de utf8. Confusamente, utf8es una implementación UTF-8 defectuosa de las primeras versiones de MySQL que solo queda por compatibilidad con versiones anteriores. La versión fija recibió el nombre utf8mb4.

Nota: Las versiones más recientes de MySQL han actualizado las reglas de clasificación de Unicode, disponibles con nombres como utf8mb4_0900_ai_ci reglas equivalentes basadas en Unicode 9.0, y sin una _general variante equivalente . Las personas que lean esto ahora probablemente deberían usar una de estas colaciones más nuevas en lugar de cualquiera _unicode o _general . Gran parte de lo que está escrito a continuación ya no es de gran interés si puedes usar una de las colaciones más nuevas.

Diferencias clave

  • utf8mb4_unicode_ci se basa en las reglas oficiales de Unicode para la clasificación y comparación universales, que se clasifican con precisión en una amplia gama de idiomas.

  • utf8mb4_general_cies un conjunto simplificado de reglas de clasificación que tiene como objetivo hacerlo tan bien como sea posible mientras toma muchos atajos diseñados para mejorar la velocidad. No sigue las reglas de Unicode y resultará en una clasificación o comparación indeseable en algunas situaciones, como cuando se usan idiomas o caracteres particulares.

    En los servidores modernos, este aumento de rendimiento será casi insignificante. Fue ideado en un momento en que los servidores tenían una pequeña fracción del rendimiento de la CPU de las computadoras de hoy.

Beneficios de utf8mb4_unicode_cimás deutf8mb4_general_ci

utf8mb4_unicode_ci, que utiliza las reglas Unicode para la clasificación y la comparación, emplea un algoritmo bastante complejo para la clasificación correcta en una amplia gama de idiomas y cuando se utiliza una amplia gama de caracteres especiales. Estas reglas deben tener en cuenta las convenciones específicas del idioma; no todos clasifican sus personajes en lo que llamaríamos "orden alfabético".

En lo que respecta a los idiomas latinos (es decir, "europeos"), no hay mucha diferencia entre la ordenación Unicode y la utf8mb4_general_ciordenación simplificada en MySQL, pero aún existen algunas diferencias:

  • Por ejemplo, la intercalación Unicode ordena "ß" como "ss" y "Œ" como "OE" como lo desearían normalmente las personas que usan esos caracteres, mientras que los utf8mb4_general_ciclasifica como caracteres individuales (presumiblemente como "s" y "e" respectivamente) .

  • Algunos caracteres Unicode se definen como ignorables, lo que significa que no deberían contar para el orden de clasificación y la comparación debería pasar al siguiente carácter. utf8mb4_unicode_cimaneja estos adecuadamente.

En los idiomas no latinos, como los idiomas asiáticos o los idiomas con diferentes alfabetos, puede haber muchas más diferencias entre la ordenación Unicode y la utf8mb4_general_ciordenación simplificada . La idoneidad de utf8mb4_general_cidependerá en gran medida del lenguaje utilizado. Para algunos idiomas, será bastante inadecuado.

¿Qué deberías usar?

Es casi seguro que ya no hay razón para usar utf8mb4_general_ci, ya que hemos dejado atrás el punto donde la velocidad de la CPU es lo suficientemente baja como para que la diferencia de rendimiento sea importante. Es casi seguro que su base de datos estará limitada por otros cuellos de botella además de este.

En el pasado, algunas personas recomendaban usar, utf8mb4_general_ciexcepto cuando la clasificación precisa sería lo suficientemente importante como para justificar el costo de rendimiento. Hoy, ese costo de rendimiento casi ha desaparecido, y los desarrolladores están tratando la internacionalización con más seriedad.

Hay que argumentar que si la velocidad es más importante para usted que la precisión, es mejor que no haga ningún tipo de clasificación. Es trivial hacer un algoritmo más rápido si no necesita que sea preciso. Por lo tanto, utf8mb4_general_cies un compromiso que probablemente no sea necesario por razones de velocidad y probablemente tampoco sea adecuado por razones de precisión.

Otra cosa que agregaré es que incluso si sabe que su aplicación solo admite el idioma inglés, es posible que deba tratar con los nombres de las personas, que a menudo pueden contener caracteres utilizados en otros idiomas en los que es tan importante ordenarlos correctamente . El uso de las reglas de Unicode para todo ayuda a tener la tranquilidad de saber que las personas muy inteligentes de Unicode han trabajado muy duro para que la clasificación funcione correctamente.

Que significan las partes

En primer lugar, cies para la clasificación y comparación sin distinción entre mayúsculas y minúsculas . Esto significa que es adecuado para datos textuales, y el caso no es importante. Los otros tipos de cotejo son cs( distingue entre mayúsculas y minúsculas) para datos textuales donde el caso es importante y bin, para donde la codificación debe coincidir, bit por bit, que es adecuado para campos que realmente son datos binarios codificados (incluyendo, por ejemplo, Base64). La clasificación sensible a mayúsculas y minúsculas conduce a algunos resultados extraños y la comparación sensible a mayúsculas y minúsculas puede dar lugar a valores duplicados que difieren solo en mayúsculas y minúsculas, por lo que las clasificaciones sensibles a mayúsculas y minúsculas caen en desuso para los datos textuales; si el caso es significativo para usted, entonces la puntuación es ignorable etc., probablemente también sea significativo, y una intercalación binaria podría ser más apropiada.

A continuación, unicodeo se generalrefiere a las reglas específicas de clasificación y comparación, en particular, la forma en que el texto se normaliza o compara. Hay muchos conjuntos diferentes de reglas para la codificación de caracteres utf8mb4, unicodey generalson dos que intentan funcionar bien en todos los idiomas posibles en lugar de uno específico. Las diferencias entre estos dos conjuntos de reglas son el tema de esta respuesta. Tenga en cuenta que unicodeusa reglas de Unicode 4.0. Las versiones recientes de MySQL agregan los conjuntos de unicode_520reglas usando reglas de Unicode 5.2 y 0900(descartando la parte "unicode_") usando reglas de Unicode 9.0.

Y, por último, utf8mb4es la codificación de caracteres utilizada internamente. En esta respuesta, solo estoy hablando de codificaciones basadas en Unicode.

thomasrutter
fuente
218
@KahWeeTeng Debe Nunca, nunca usar utf8_general_ci: simplemente no funciona. Es un retroceso a los viejos tiempos malos de la abstinencia ASCII de hace cincuenta años. La coincidencia sin distinción entre mayúsculas y minúsculas Unicode no se puede realizar sin el mapa de mayúsculas y minúsculas del UCD. Por ejemplo, "Σίσυφος" tiene tres sigmas diferentes en él; o cómo la minúscula de "TSCHüẞ" es "tschüβ", pero la mayúscula de "tschüβ" es "TSCHÜSS". Puedes tener razón o puedes ser rápido. Por lo tanto, debe usar utf8_unicode_ci, porque si no le importa la corrección, entonces es trivial hacerlo infinitamente rápido.
tchrist
77
Después de leer esto, también descubrí que utf8_unicode_ci considerará que cualquier carácter con el mismo peso de colación sea igual para fines de comparación de igualdad. Esto lleva a casos donde "か" == "が"o "ǽ" == "æ". Para ordenar esto tiene sentido, pero podría ser sorprendente al seleccionar a través de igualdades o lidiar con índices únicos - bugs.mysql.com/bug.php?id=16526
Mat Schaffer
44
@DanHorvat La única razón práctica para limitarse al subconjunto Unicode más antiguo y limitado de MySQL es si tiene una versión anterior de MySQL que no admite el utf8mb4 más completo. 5.5.3 tiene más de 5 años. Soy consciente de que Plesk se ejecuta en un horario de MySQL diferente, pero la mayoría de distribuciones son en MySQL 5.5 ahora y Plesk 11.x lo hace el soporte de MySQL 5.5 si actualiza sus componentes.
thomasrutter
22
No estoy de acuerdo con que usar la variante más nueva y más estándar sea una mala práctica, y creo que es inflamatorio llamar a la gente malos desarrolladores por algo como esto. También es posible que desee tener en cuenta que mi respuesta tal como está dice " en las nuevas versiones de MySQL use utf8mb4, en lugar de utf8", énfasis mío.
thomasrutter
24
@DanHorvat utf8mb4es la única opción correcta . Contigo utf8está atascado en alguna variante de 3 bytes de UTF8 solo de MySQL que solo MySQL (y MariaDB) saben qué hacer. El resto del mundo está utilizando UTF8, que puede contener hasta 4 bytes por carácter . Los desarrolladores de MySQL nombraron erróneamente su codificación homebrew utf8y para no romper la compatibilidad con versiones anteriores, ahora deben referirse al UTF8 real como utf8mb4.
Stijn de Witt
162

Quería saber cuál es la diferencia de rendimiento entre usar utf8_general_ciy utf8_unicode_ci, pero no encontré ningún punto de referencia en Internet, así que decidí crearlo.

Creé una tabla muy simple con 500,000 filas:

CREATE TABLE test(
  ID INT(11) DEFAULT NULL,
  Description VARCHAR(20) DEFAULT NULL
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;

Luego lo llené con datos aleatorios ejecutando este procedimiento almacenado:

CREATE PROCEDURE randomizer()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE random CHAR(20) ;
  theloop: loop
    SET random = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
    INSERT INTO test VALUES (i+1, random);
    SET i=i+1;
    IF i = 500000 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END

Luego creé los siguientes procedimientos almacenados para comparar de manera simple SELECT, SELECTcon LIKEy ordenar ( SELECTcon ORDER BY):

CREATE PROCEDURE benchmark_simple_select()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description = 'test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_select_like()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description LIKE '%test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_order_by()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE ID > FLOOR(1 + RAND() * (400000 - 1))
    ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;
    SET i = i + 1;
    IF i = 10 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

En los procedimientos almacenados utf8_general_cise utiliza la clasificación anterior , pero, por supuesto, durante las pruebas utilicé ambos utf8_general_ciy utf8_unicode_ci.

Llamé a cada procedimiento almacenado 5 veces para cada colación (5 veces para utf8_general_ciy 5 veces para utf8_unicode_ci) y luego calculé los valores promedio.

Mis resultados son:

benchmark_simple_select()

  • con utf8_general_ci: 9,957 ms
  • con utf8_unicode_ci: 10,271 ms

En este punto de referencia, el uso utf8_unicode_cies más lento que utf8_general_cien un 3,2%.

benchmark_select_like()

  • con utf8_general_ci: 11,441 ms
  • con utf8_unicode_ci: 12,811 ms

En este punto de referencia, el uso utf8_unicode_cies más lento que utf8_general_cien un 12%.

benchmark_order_by()

  • con utf8_general_ci: 11,944 ms
  • con utf8_unicode_ci: 12,887 ms

En este punto de referencia, el uso utf8_unicode_cies más lento que utf8_general_cien un 7,9%.

codificador nocturno
fuente
16
Buen punto de referencia, gracias por compartir. Estoy obteniendo cifras sensiblemente similares (MySQL v5.6.12 en Windows): 10%, 4%, 8%. Estoy de acuerdo: la ganancia de rendimiento de utf8_general_cies demasiado mínima para que valga la pena usarla.
RandomSeed
10
1) ¿Pero no debería este punto de referencia generar resultados similares para las dos colaciones por definición? Me refiero a que CONV(FLOOR(RAND() * 99999999999999), 20, 36)solo genera ASCII, y ningún carácter Unicode para ser procesado por los algoritmos de las intercalaciones. 2) Description = 'test' COLLATE ...y Description LIKE 'test%' COLLATE ...solo procesan una sola cadena ("prueba") en tiempo de ejecución, ¿no? 3) En aplicaciones reales, las columnas utilizadas en la ordenación probablemente se indexarían, y la velocidad de indexación en diferentes intercalaciones con texto real no ASCII podría diferir.
Halil Özgür
2
@ HalilÖzgür: su punto está parcialmente equivocado. Supongo que no se trata de que el valor del punto de código esté fuera de ASCII (que general_ci manejaría correctamente), sino de características específicas, como tratar diéresis escritos como "Uml ea ute" o algunas sutilezas similares.
Tomasz Gandor
38

Esta publicación lo describe muy bien.

En resumen: utf8_unicode_ci usa el Algoritmo de clasificación Unicode como se define en los estándares Unicode, mientras que utf8_general_ci es un orden de clasificación más simple que resulta en resultados de clasificación "menos precisos".

Michael Madsen
fuente
1
Gracias. Esa fue mi impresión. tomaré el éxito de rendimiento :)
onassar 01 de
77
Si no le importa la corrección, es trivial hacer que cualquier algoritmo sea infinitamente rápido. Solo usa utf8_unicode_ciy finge que el otro no existe.
tchrist
1
@tchrist pero si te importa un cierto equilibrio entre la corrección y la velocidad, utf8_general_cipuede ser para ti
Shelvacu
@tchrist Nunca te conviertas en un programador de juegos;)
Stijn de Witt
1
@onassar: MySQL 8.0 afirma haber mejorado significativamente el rendimiento de todas las intercalaciones.
Rick James
9

Consulte el manual de mysql, sección Juegos de caracteres Unicode :

Para cualquier conjunto de caracteres Unicode, las operaciones realizadas con la clasificación _general_ci son más rápidas que las de la clasificación _unicode_ci. Por ejemplo, las comparaciones para la colación utf8_general_ci son más rápidas, pero un poco menos correctas, que las comparaciones para utf8_unicode_ci. La razón de esto es que utf8_unicode_ci admite mapeos como expansiones; es decir, cuando un personaje se compara como igual a combinaciones de otros personajes. Por ejemplo, en alemán y otros idiomas, "ß" es igual a "ss". utf8_unicode_ci también admite contracciones y caracteres ignorables. utf8_general_ci es una recopilación heredada que no admite expansiones, contracciones o caracteres ignorables. Solo puede hacer comparaciones uno a uno entre los personajes.

Para resumir, utf_general_ci utiliza un conjunto de comparaciones más pequeño y menos correcto (según el estándar) que utf_unicode_ci que debería implementar todo el estándar. El conjunto general_ci será más rápido porque hay menos cálculos que hacer.

Dana la sana
fuente
18
No existe tal cosa como "un poco menos correcto". La corrección es una característica booleana; no admite modificadores de grado. Solo usa utf8_unicode_ciy finge que la versión defectuosa no existe.
tchrist
2
Tuve problemas para obtener 5.6.15 para tomar la configuración collation_connection, y resulta que tienes que pasarlo en la línea SET como 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'. El crédito va a Mathias Bynens por la solución, aquí está su guía muy útil: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert
44
@tchrist El problema con decir que la corrección es booleana es que no tiene en cuenta situaciones que no dependen de la corrección absoluta. Su punto subyacente no es inválido ni intento exponer los beneficios de general_ci, pero su afirmación general sobre la corrección se refuta fácilmente. Lo hago a diario en mi profesión. Dejando de lado la comedia, Stuart tiene un buen punto aquí .
Anthony
55
Con la geolocalización o el desarrollo del juego, intercambiamos la corrección con el rendimiento todo el tiempo. Y, por supuesto, la corrección es un número real entre 0y 1, no un bool. :) Por ejemplo, seleccionar puntos geográficos en un cuadro delimitador es una aproximación de 'puntos cercanos' que no es tan bueno como calcular la distancia entre el punto y el punto de referencia y filtrar en eso. Pero ambos son una aproximación y, de hecho, la corrección completa no es posible en su mayoría. Vea la paradoja de la costa y el IEEE 754
Stijn de Witt
44
TL; DR : Proporcione un programa que imprima el resultado correcto para1/3
Stijn de Witt
7

En pocas palabras:

Si necesita un mejor orden de clasificación, use utf8_unicode_ci(este es el método preferido),

pero si está completamente interesado en el rendimiento, úselo utf8_general_ci, pero sepa que está un poco desactualizado.

Las diferencias en términos de rendimiento son muy leves.

simhumileco
fuente
1
Ambos están desactualizados ahora - vea la respuesta aceptada para más información
thomasrutter
OK, gracias @thomasrutter
simhumileco
6

Algunos detalles (PL)

Como podemos leer aquí ( Peter Gulutzan ) hay una diferencia en la clasificación / comparación de la letra polaca "Ł" (L con trazo - html esc:) Ł(minúscula: "ł" - html esc:) ł- tenemos la siguiente suposición:

utf8_polish_ci      Ł greater than L and less than M
utf8_unicode_ci     Ł greater than L and less than M
utf8_unicode_520_ci Ł equal to L
utf8_general_ci     Ł greater than Z

En idioma polaco, la letra Łes después de la letra Ly antes M. Ninguno de estos códigos es mejor o peor, depende de sus necesidades.

Kamil Kiełczewski
fuente
1

Hay dos grandes diferencias en la clasificación y la coincidencia de caracteres:

Clasificación :

  • utf8mb4_general_ci elimina todos los acentos y los ordena uno por uno, lo que puede crear resultados de clasificación incorrectos.
  • utf8mb4_unicode_ci tipo exacto.

Coincidencia de personajes

Coinciden con los personajes de manera diferente.

Por ejemplo, en utf8mb4_unicode_ciusted tiene i != ı, pero en utf8mb4_general_ciél tieneı=i .

Por ejemplo, imagina que tienes una fila con name="Yılmaz". Entonces

select id from users where name='Yilmaz';

devolvería la fila si la colocación es utf8mb4_general_ci, pero si está colocada con utf8mb4_unicode_ciella no lo haría devolvería la fila!

Por otro lado tenemos eso a=ªy ß=ssen lo utf8mb4_unicode_cique no es el caso utf8mb4_general_ci. Entonces imagina que tienes una pelea con name="ªßi", entonces

select id from users where name='assi';

devolvería la fila si la colocación es utf8mb4_unicode_ci, pero no devolvería una fila si la colocación se establece enutf8mb4_general_ci .

Puede encontrar una lista completa de coincidencias para cada colocación aquí .

Adán
fuente