Qual é a diferença entre utf8_general_ci e utf8_unicode_ci?

1063

Entre utf8_general_cie utf8_unicode_ci, existem diferenças em termos de desempenho?

KahWee Teng
fonte
1
Veja também stackoverflow.com/questions/1036454/…
unor 28/08
6
Se você gosta utf8[mb4]_unicode_ci, você pode gostar utf8[mb4]_unicode_520_ciainda mais.
Rick James
8
Não sei como me sinto sobre isso - em vez de corrigir sua implementação para seguir o padrão Unicode mais recente, eles mantêm a versão obsoleta como padrão e as pessoas precisam adicionar "520" para usar a correta agora. E não é compatível com versões anteriores e anteriores, porque você não pode usar a versão "520" nas versões mais antigas do MySQL. Por que eles não podem ter acabado de atualizar seu agrupamento existente? Mesmo com "mb4", realmente. Qual código realmente dependia do antigo comportamento limitado / obsoleto para justificar manter isso como padrão?
thomasrutter
7
Ainda melhor é o padrão de 8.0 utf8mb4_0900_ai_ci.
196 Rick Rick

Respostas:

1591

Esses dois agrupamentos são para a codificação de caracteres UTF-8. As diferenças estão em como o texto é classificado e comparado.

Nota: No MySQL você precisa usar em utf8mb4vez de utf8. Confusamente, utf8é uma implementação falha de UTF-8 das primeiras versões do MySQL que permanece apenas para compatibilidade com versões anteriores. A versão fixa recebeu o nome utf8mb4.

Nota: As versões mais recentes do MySQL atualizam as regras de classificação Unicode, disponíveis sob nomes como utf8mb4_0900_ai_ci regras equivalentes baseadas no Unicode 9.0 - e sem _general variante equivalente . As pessoas que estão lendo isso agora provavelmente devem usar um desses agrupamentos mais recentes, em vez de um _unicode ou outro_general . Muito do que está escrito abaixo não é mais de grande interesse se você puder usar um dos agrupamentos mais recentes.

Principais diferenças

  • utf8mb4_unicode_ci baseia-se nas regras oficiais do Unicode para classificação e comparação universal, que são classificadas com precisão em uma ampla variedade de idiomas.

  • utf8mb4_general_cié um conjunto simplificado de regras de classificação que tem o objetivo de fazer o melhor possível, além de tomar muitos atalhos projetados para melhorar a velocidade. Ele não segue as regras Unicode e resultará em classificação ou comparação indesejável em algumas situações, como ao usar idiomas ou caracteres específicos.

    Em servidores modernos, esse aumento de desempenho será praticamente insignificante. Foi desenvolvido em uma época em que os servidores tinham uma pequena fração do desempenho da CPU dos computadores atuais.

Benefícios de utf8mb4_unicode_cimais deutf8mb4_general_ci

utf8mb4_unicode_ci, que usa as regras Unicode para classificação e comparação, emprega um algoritmo bastante complexo para a classificação correta em uma ampla variedade de idiomas e ao usar uma ampla variedade de caracteres especiais. Essas regras precisam levar em consideração as convenções específicas do idioma; nem todo mundo classifica seus personagens no que chamaríamos de "ordem alfabética".

No que diz respeito às línguas latinas (ou seja, "européias"), não há muita diferença entre a classificação Unicode e a utf8mb4_general_ciclassificação simplificada no MySQL, mas ainda existem algumas diferenças:

  • Por exemplo, o agrupamento Unicode classifica "ß" como "ss" e "Œ" como "OE" como as pessoas que usavam esses caracteres normalmente desejariam, enquanto os utf8mb4_general_ciclassifica como caracteres únicos (presumivelmente como "s" e "e", respectivamente) .

  • Alguns caracteres Unicode são definidos como ignoráveis, o que significa que eles não devem contar para a ordem de classificação e a comparação deve passar para o próximo caractere. utf8mb4_unicode_cilida com isso corretamente.

Em idiomas não latinos, como idiomas asiáticos ou com alfabetos diferentes, pode haver muito mais diferenças entre a classificação Unicode e a utf8mb4_general_ciclassificação simplificada . A adequação de utf8mb4_general_cidependerá muito do idioma usado. Para alguns idiomas, será bastante inadequado.

O que você deve usar?

Não há quase nenhuma razão para usar utf8mb4_general_cimais, pois deixamos para trás o ponto em que a velocidade da CPU é baixa o suficiente para que a diferença de desempenho seja importante. Seu banco de dados quase certamente será limitado por outros gargalos além disso.

No passado, algumas pessoas recomendavam o uso, utf8mb4_general_ciexceto quando a classificação precisa seria importante o suficiente para justificar o custo de desempenho. Hoje, esse custo de desempenho praticamente desapareceu e os desenvolvedores estão tratando a internacionalização mais a sério.

Há um argumento a ser argumentado de que, se a velocidade é mais importante para você do que a precisão, você também não pode fazer nenhuma classificação. É trivial tornar um algoritmo mais rápido se você não precisar que seja preciso. Portanto, utf8mb4_general_cié um compromisso que provavelmente não é necessário por motivos de velocidade e provavelmente também não é adequado por motivos de precisão.

Outra coisa a acrescentar é que, mesmo que você saiba que seu aplicativo é compatível apenas com o idioma inglês, ele ainda pode precisar lidar com os nomes das pessoas, que geralmente podem conter caracteres usados ​​em outros idiomas nos quais é tão importante classificar corretamente . O uso das regras Unicode para tudo ajuda a tranqüilizar o fato de que as pessoas Unicode muito inteligentes trabalharam muito para fazer a classificação funcionar corretamente.

O que as partes significam

Em primeiro lugar, cié para classificação e comparação que não diferenciam maiúsculas de minúsculas . Isso significa que é adequado para dados textuais e o caso não é importante. Os outros tipos de intercalação são cs(com distinção entre maiúsculas e minúsculas) para dados de texto onde maiúsculas e minúsculas são importantes e bin, para onde a codificação precisa corresponder, bit por bit, o que é adequado para campos que são realmente dados binários codificados (incluindo, por exemplo, Base64). A classificação com distinção entre maiúsculas e minúsculas leva a alguns resultados estranhos e a comparação com distinção entre maiúsculas e minúsculas pode resultar em valores duplicados que diferem apenas em maiúsculas e minúsculas; portanto, os agrupamentos com diferenciação de maiúsculas e minúsculas estão desvalorizando os dados de texto - se maiúsculas e minúsculas forem importantes para você, pontuação de outra forma ignorável e assim por diante provavelmente também é significativo, e um agrupamento binário pode ser mais apropriado.

A seguir, unicodeou generalrefere-se às regras específicas de classificação e comparação - em particular, a maneira como o texto é normalizado ou comparado. Existem muitos conjuntos diferentes de regras para a codificação de caracteres utf8mb4, com unicodee generalsendo dois que tentam funcionar bem em todos os idiomas possíveis, em vez de um específico. As diferenças entre esses dois conjuntos de regras são o assunto desta resposta. Observe que unicodeusa regras do Unicode 4.0. Versões recentes do MySQL adicionam os conjuntos de regras unicode_520usando regras do Unicode 5.2 e 0900(removendo a parte "unicode_") usando regras do Unicode 9.0.

E, finalmente, utf8mb4é claro que a codificação de caracteres é usada internamente. Nesta resposta, estou falando apenas de codificações baseadas em Unicode.

thomasrutter
fonte
218
@KahWeeTeng Você deve nunca mais, nunca use utf8_general_ci: ele simplesmente não funciona. É um retrocesso aos maus e velhos tempos da estagnação ASCII de cinquenta anos atrás. A correspondência que não diferencia maiúsculas de minúsculas de Unicode não pode ser feita sem o mapa de dobras do UCD. Por exemplo, “Σίσυφος” possui três sigmas diferentes; ou como a minúscula de "TSCHüẞ" é "tschüβ", mas a maiúscula de "tschüβ" é "TSCHÜSS". Você pode estar certo ou ser rápido. Portanto, você deve usar utf8_unicode_ci, porque se você não se importa com a correção, é trivial torná-la infinitamente rápida.
tchrist
7
Depois de ler isso, também descobri que utf8_unicode_ci considerará qualquer caractere com o mesmo peso de agrupamento igual para fins de comparação de igualdade. Isso leva a casos em que "か" == "が"ou"ǽ" == "æ" . Para classificar isso faz sentido, mas pode ser surpreendente quando selecionando via igualdades ou lidar com índices únicos - bugs.mysql.com/bug.php?id=16526
Mat Schaffer
4
@DanHorvat A única razão prática para limitar-se ao subconjunto mais antigo e mais limitado do Unicode do MySQL é se você tiver uma versão antiga do MySQL que não suporta o utf8mb4 mais completo. 5.5.3 tem mais de 5 anos. Aprecio que o Plesk seja executado em uma programação diferente do MySQL, mas a maioria das distros está no MySQL 5.5 agora e no Plesk 11.x faz suporte ao MySQL 5.5 se você atualizar seus componentes.
thomasrutter
22
Eu discordaria que o uso da variante mais nova e de reclamação de padrões seja uma prática ruim e acho que é inflamatório chamar as pessoas de desenvolvedores ruins por algo assim. Você também pode notar que minha resposta, como está, diz " em novas versões do MySQL, use utf8mb4, em vez de utf8", enfatizando a minha.
thomasrutter
24
@ DanHorvat utf8mb4é a única opção correta . Com utf8você está preso em alguma variante UTF8 de apenas 3 bytes do MySQL que apenas o MySQL (e o MariaDB) sabem o que fazer. O resto do mundo está usando UTF8, que pode conter até 4 bytes por caractere . Os desenvolvedores do MySQL nomearam incorretamente sua codificação de homebrew utf8e, para não quebrar a compatibilidade com versões anteriores, agora precisam se referir ao UTF8 real como utf8mb4.
Stijn de Witt
162

Eu queria saber qual é a diferença de desempenho entre usar utf8_general_cie utf8_unicode_ci, mas não encontrei nenhum benchmark listado na internet, então decidi criar eu mesmo.

Criei uma tabela muito simples com 500.000 linhas:

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

Então eu preenchi com dados aleatórios executando este procedimento armazenado:

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

Em seguida, criei os seguintes procedimentos armazenados para fazer benchmark simples SELECT, SELECTcom LIKEe classificação ( SELECTcom 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;

Nos procedimentos armazenados acima, o utf8_general_ciagrupamento é usado, mas é claro que durante os testes eu usei ambos utf8_general_cie utf8_unicode_ci.

Chamei cada procedimento armazenado 5 vezes para cada agrupamento (5 vezes para utf8_general_cie 5 vezes para utf8_unicode_ci) e depois calculei os valores médios.

Meus resultados são:

benchmark_simple_select()

  • com utf8_general_ci: 9.957 ms
  • com utf8_unicode_ci: 10.271 ms

Nesse benchmark, o uso utf8_unicode_cié mais lento que utf8_general_ciem 3,2%.

benchmark_select_like()

  • com utf8_general_ci: 11.441 ms
  • com utf8_unicode_ci: 12.811 ms

Nesse benchmark, o uso utf8_unicode_cié mais lento que utf8_general_ciem 12%.

benchmark_order_by()

  • com utf8_general_ci : 11.944 ms
  • com utf8_unicode_ci: 12.887 ms

Nesse benchmark, o uso utf8_unicode_cié mais lento que utf8_general_ciem 7,9%.

nightcoder
fonte
16
Bom benchmark, obrigado por compartilhar. Estou recebendo números sensivelmente semelhantes (MySQL v5.6.12 no Windows): 10%, 4%, 8%. Concordo: o ganho de desempenho utf8_general_cié mínimo demais para valer a pena usar.
randomSeed
10
1) Mas esse benchmark não deve gerar resultados semelhantes para os dois agrupamentos, por definição? Quero dizer, CONV(FLOOR(RAND() * 99999999999999), 20, 36)gera apenas ASCII e nenhum caractere Unicode a ser processado pelos algoritmos dos agrupamentos. 2) Description = 'test' COLLATE ...e Description LIKE 'test%' COLLATE ...processa apenas uma única string ("teste") em tempo de execução, não é? 3) Em aplicativos reais, as colunas usadas na ordenação provavelmente seriam indexadas e a velocidade de indexação em diferentes agrupamentos com texto não-ASCII real pode ser diferente.
Halil Özgür
2
@ HalilÖzgür - seu ponto de vista está parcialmente errado. Eu acho que não é sobre o valor codepoint ser ASCII fora (que general_ci iria lidar corretamente), mas sobre recursos específicos, como o tratamento tremas escritos como "Uml ea ute" ou alguns tais sutilezas.
Tomasz Gandor
38

Esta postagem descreve muito bem.

Em resumo: utf8_unicode_ci usa o algoritmo de agrupamento Unicode, conforme definido nos padrões Unicode, enquanto utf8_general_ci é uma ordem de classificação mais simples, que resulta em resultados de classificação "menos precisos".

Michael Madsen
fonte
1
obrigado. essa foi a minha impressão. Vou levar o acerto de desempenho :)
onassar
7
Se você não se importa com a correção, é trivial tornar qualquer algoritmo infinitamente rápido. Basta usar utf8_unicode_cie fingir que o outro não existe.
tchrist
1
@tchrist mas se você se preocupa com um certo equilíbrio entre exatidão e velocidade, utf8_general_cipode ser para você
Shelvacu
@tchrist Nunca se tornar um programador de jogos;)
Stijn de Witt
1
@onassar - O MySQL 8.0 afirma ter melhorado significativamente o desempenho de todos os agrupamentos.
Rick James
9

Veja o manual do mysql, seção Unicode Character Sets :

Para qualquer conjunto de caracteres Unicode, as operações executadas usando o agrupamento _general_ci são mais rápidas que as do agrupamento _unicode_ci. Por exemplo, as comparações para o agrupamento utf8_general_ci são mais rápidas, mas um pouco menos corretas, do que as comparações para utf8_unicode_ci. A razão para isso é que utf8_unicode_ci suporta mapeamentos como expansões; isto é, quando um caractere é comparado com combinações de outros caracteres. Por exemplo, em alemão e em alguns outros idiomas "ß" é igual a "ss". utf8_unicode_ci também suporta contrações e caracteres ignoráveis. utf8_general_ci é um agrupamento herdado que não suporta expansões, contrações ou caracteres ignoráveis. Ele pode fazer apenas comparações individuais entre os caracteres.

Portanto, para resumir, utf_general_ci usa um conjunto de comparações menor e menos correto (de acordo com o padrão) do que utf_unicode_ci, que deve implementar todo o padrão. O conjunto general_ci será mais rápido porque há menos cálculo a ser feito.

Dana the Sane
fonte
18
Não existe algo como "um pouco menos correto". A correção é uma característica booleana; não admite modificadores de grau. Basta usar utf8_unicode_cie fingir que a versão quebrada com erros não existe.
22412 tchrist
2
Eu tive problemas ao obter o 5.6.15 para pegar a configuração collation_connection, e você deve passá-la na linha SET, como 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'. O crédito é para Mathias Bynens pela solução, eis o seu guia muito útil: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert
4
@tchrist O problema de dizer que a correção é booleana é que ela não leva em consideração situações que não dependem da correção absoluta. Seu ponto subjacente não é inválido nem estou tentando defender os benefícios do general_ci, mas sua afirmação geral sobre correção é facilmente refutada. Faço isso diariamente em minha profissão. Comédia à parte, Stuart tem um bom argumento aqui .
Anthony
5
Com geolocalização ou desenvolvimento de jogos, trocamos correção com desempenho o tempo todo. E, claro, a correção é um número real entre 0e 1, não um bool. :) Por exemplo, selecionar pontos geográficos em uma caixa delimitadora é uma aproximação de 'pontos próximos', o que não é tão bom quanto calcular a distância entre o ponto e o ponto de referência e filtrá-lo. Mas ambos são uma aproximação e, de fato, a correção total não é alcançável. Veja o paradoxo da costa e o IEEE 754
Stijn de Witt
4
TL; DR : forneça um programa que imprima o resultado correto para:1/3
Stijn de Witt
7

Em poucas palavras:

Se você precisar de uma ordem de classificação melhor - use utf8_unicode_ci(este é o método preferido),

mas se você estiver totalmente interessado em desempenho - use utf8_general_ci, mas saiba que está um pouco desatualizado.

As diferenças em termos de desempenho são muito pequenas.

simhumileco
fonte
1
Ambos estão desatualizados agora - veja a resposta aceita por mais
thomasrutter
OK, obrigado @thomasrutter
simhumileco
6

Alguns detalhes (PL)

Como podemos ler aqui ( Peter Gulutzan ), há uma diferença na classificação / comparação da letra polonesa "Ł" (L com acidente vascular cerebral - html esc:) Ł(letras minúsculas: "ł" - html esc:) ł- temos a seguinte suposição:

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

Na língua polonesa, a letra Łé depois da letra Le antes M. Ninguém nessa codificação é melhor ou pior - depende de suas necessidades.

Kamil Kiełczewski
fonte
1

Há duas grandes diferenças na classificação e na correspondência de caracteres:

Classificação :

  • utf8mb4_general_ci remove todos os acentos e classificações, uma por uma, o que pode criar resultados de classificação incorretos.
  • utf8mb4_unicode_ci classifica preciso.

Correspondência de caracteres

Eles combinam caracteres de maneira diferente.

Por exemplo, utf8mb4_unicode_civocê tem i != ı, mas utf8mb4_general_cimantémı=i .

Por exemplo, imagine que você tem uma briga com name="Yılmaz". Então

select id from users where name='Yilmaz';

retornaria a linha se a colocação for utf8mb4_general_ci, mas se for colocada com utf8mb4_unicode_ciela, não retornará a linha!

Por outro lado, temos isso a=ªe ß=ssem utf8mb4_unicode_cique não é o caso utf8mb4_general_ci. Imagine que você tem uma briga com name="ªßi", então

select id from users where name='assi';

retornaria a linha se a colocação for utf8mb4_unicode_ci, mas não retornaria uma linha se a colocação estiver definida comoutf8mb4_general_ci .

Uma lista completa de correspondências para cada colocação pode ser encontrada aqui .

Adão
fonte