Entre utf8_general_ci
e utf8_unicode_ci
, existem diferenças em termos de desempenho?
utf8[mb4]_unicode_ci
, você pode gostar utf8[mb4]_unicode_520_ci
ainda mais.
utf8mb4_0900_ai_ci
.
Entre utf8_general_ci
e utf8_unicode_ci
, existem diferenças em termos de desempenho?
utf8[mb4]_unicode_ci
, você pode gostar utf8[mb4]_unicode_520_ci
ainda mais.
utf8mb4_0900_ai_ci
.
Respostas:
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 utf8mb4
vez 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_ci
mais 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_ci
classificaçã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_ci
classifica 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_ci
lida 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_ci
classificação simplificada . A adequação de utf8mb4_general_ci
dependerá 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_ci
mais, 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_ci
exceto 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, unicode
ou general
refere-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 unicode
e general
sendo 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 unicode
usa regras do Unicode 4.0. Versões recentes do MySQL adicionam os conjuntos de regras unicode_520
usando 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.
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.
"か" == "が"
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
utf8mb4
é a única opção correta . Com utf8
você 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 utf8
e, para não quebrar a compatibilidade com versões anteriores, agora precisam se referir ao UTF8 real como utf8mb4
.
Eu queria saber qual é a diferença de desempenho entre usar utf8_general_ci
e 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
, SELECT
com LIKE
e classificação ( SELECT
com 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_ci
agrupamento é usado, mas é claro que durante os testes eu usei ambos utf8_general_ci
e utf8_unicode_ci
.
Chamei cada procedimento armazenado 5 vezes para cada agrupamento (5 vezes para utf8_general_ci
e 5 vezes para utf8_unicode_ci
) e depois calculei os valores médios.
Meus resultados são:
benchmark_simple_select()
utf8_general_ci
: 9.957 ms utf8_unicode_ci
: 10.271 ms Nesse benchmark, o uso utf8_unicode_ci
é mais lento que utf8_general_ci
em 3,2%.
benchmark_select_like()
utf8_general_ci
: 11.441 ms utf8_unicode_ci
: 12.811 ms Nesse benchmark, o uso utf8_unicode_ci
é mais lento que utf8_general_ci
em 12%.
benchmark_order_by()
utf8_general_ci
: 11.944 ms utf8_unicode_ci
: 12.887 ms Nesse benchmark, o uso utf8_unicode_ci
é mais lento que utf8_general_ci
em 7,9%.
utf8_general_ci
é mínimo demais para valer a pena usar.
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.
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".
utf8_unicode_ci
e fingir que o outro não existe.
utf8_general_ci
pode ser para você
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.
utf8_unicode_ci
e fingir que a versão quebrada com erros não existe.
0
e 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
1/3
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.
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 L
e antes M
. Ninguém nessa codificação é melhor ou pior - depende de suas necessidades.
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_ci
você tem i != ı
, mas utf8mb4_general_ci
manté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_ci
ela, não retornará a linha!
Por outro lado, temos isso a=ª
e ß=ss
em utf8mb4_unicode_ci
que 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 .
De acordo com este post, há um benefício de desempenho consideravelmente grande no MySQL 5.7 ao usar utf8mb4_general_ci em vez de utf8mb4_unicode_ci: https://www.percona.com/blog/2019/02/27/charset-and-collation-settings-impact -on-mysql-performance /