Qual è la differenza tra utf8_general_ci e utf8_unicode_ci?


1063

Tra utf8_general_cie utf8_unicode_ci, ci sono differenze in termini di prestazioni?



6
Se ti piace utf8[mb4]_unicode_ci, ti potrebbe piacere utf8[mb4]_unicode_520_cianche di più.
Rick James,

8
Non so come mi sento al riguardo - invece di correggere la loro implementazione per seguire l'ultimo standard Unicode mantengono la versione obsoleta come predefinita e le persone devono aggiungere "520" per usare quella corretta ora. E non è compatibile in avanti e indietro perché non è possibile utilizzare la versione "520" su versioni precedenti di MySQL. Perché non hanno appena aggiornato le regole di confronto esistenti? Lo stesso con "mb4", davvero. Quale codice dipendeva davvero dal vecchio comportamento limitato / obsoleto per giustificare il mantenimento come predefinito?
thomasrutter,

7
Ancora meglio è l'impostazione predefinita di 8.0 di utf8mb4_0900_ai_ci.
Rick James,

Risposte:


1591

Queste due regole di confronto sono entrambe per la codifica dei caratteri UTF-8. Le differenze sono nel modo in cui il testo viene ordinato e confrontato.

Nota: in MySQL devi usare utf8mb4piuttosto che utf8. Confusamente, utf8è un'implementazione UTF-8 difettosa dalle prime versioni di MySQL che rimane solo per compatibilità con le versioni precedenti. Alla versione fissa è stato dato il nome utf8mb4.

Nota: le versioni più recenti di MySQL hanno aggiornato le regole di ordinamento Unicode, disponibili con nomi come utf8mb4_0900_ai_ci per regole equivalenti basate su Unicode 9.0 - e senza _general varianti equivalenti . Le persone che leggono questo ora dovrebbero probabilmente usare una di queste più recenti regole di confronto anziché uno _unicode o _general . Gran parte di ciò che è scritto di seguito non è più molto interessante se è possibile utilizzare una delle più recenti regole di confronto.

Differenze chiave

  • utf8mb4_unicode_ci si basa sulle regole ufficiali Unicode per l'ordinamento e il confronto universali, che ordinano accuratamente in una vasta gamma di lingue.

  • utf8mb4_general_ciè un insieme semplificato di regole di smistamento che mira a fare nel miglior modo possibile adottando molte scorciatoie progettate per migliorare la velocità. Non segue le regole Unicode e comporterà l'ordinamento o il confronto indesiderato in alcune situazioni, come quando si usano particolari lingue o caratteri.

    Sui server moderni, questo aumento delle prestazioni sarà quasi trascurabile. È stato ideato in un'epoca in cui i server presentavano una piccola frazione delle prestazioni della CPU dei computer di oggi.

Vantaggi di utf8mb4_unicode_cioltreutf8mb4_general_ci

utf8mb4_unicode_ci, che utilizza le regole Unicode per l'ordinamento e il confronto, utilizza un algoritmo abbastanza complesso per l'ordinamento corretto in una vasta gamma di lingue e quando si utilizza una vasta gamma di caratteri speciali. Queste regole devono tenere conto delle convenzioni specifiche della lingua; non tutti ordinano i loro personaggi in quello che chiameremmo "ordine alfabetico".

Per quanto riguarda le lingue latine (ovvero "europee"), non c'è molta differenza tra l'ordinamento Unicode e l' utf8mb4_general_ciordinamento semplificato in MySQL, ma ci sono ancora alcune differenze:

  • Ad esempio, le regole di confronto Unicode ordinano "ß" come "ss" e "Œ" come "OE" come normalmente vorrebbero le persone che usano quei caratteri, mentre le utf8mb4_general_ciordinano come caratteri singoli (presumibilmente come "s" ed "e" rispettivamente) .

  • Alcuni caratteri Unicode sono definiti come ignorabili, il che significa che non dovrebbero contare per l'ordinamento e il confronto dovrebbe invece passare al carattere successivo. utf8mb4_unicode_cigestisce questi correttamente.

Nelle lingue non latine, come le lingue asiatiche o le lingue con alfabeti diversi, potrebbero esserci molte più differenze tra l'ordinamento Unicode e l' utf8mb4_general_ciordinamento semplificato . L'idoneità utf8mb4_general_cidipenderà fortemente dalla lingua utilizzata. Per alcune lingue, sarà abbastanza inadeguato.

Cosa dovresti usare?

Quasi certamente non c'è più motivo di utilizzarlo utf8mb4_general_ci, poiché abbiamo lasciato indietro il punto in cui la velocità della CPU è abbastanza bassa da rendere importante la differenza di prestazioni. Il tuo database sarà quasi sicuramente limitato da altri colli di bottiglia.

In passato, alcune persone raccomandavano di usare, utf8mb4_general_citranne quando uno smistamento accurato sarebbe stato abbastanza importante da giustificare il costo della prestazione. Oggi, quel costo in termini di prestazioni è quasi scomparso e gli sviluppatori stanno trattando l'internazionalizzazione più seriamente.

C'è un argomento da sostenere che se la velocità è più importante per te dell'accuratezza, puoi anche non fare alcun ordinamento. È banale rendere un algoritmo più veloce se non è necessario che sia accurato. Quindi, utf8mb4_general_ciè un compromesso che probabilmente non è necessario per motivi di velocità e probabilmente non adatto a motivi di precisione.

Un'altra cosa che aggiungerò è che anche se sai che la tua applicazione supporta solo la lingua inglese, potrebbe essere necessario gestire i nomi delle persone, che spesso possono contenere caratteri usati in altre lingue in cui è altrettanto importante ordinare correttamente . L'uso delle regole Unicode per qualsiasi cosa aiuta ad aggiungere tranquillità al fatto che le persone molto intelligenti di Unicode hanno lavorato molto duramente per far funzionare correttamente l'ordinamento.

Cosa significano le parti

In primo luogo, ciè per l' ordinamento e il confronto senza distinzione tra maiuscole e minuscole . Ciò significa che è adatto per dati testuali e il caso non è importante. Gli altri tipi di regole di confronto sono cs(con distinzione tra maiuscole e minuscole) per i dati testuali in cui il caso è importante e bin, per i casi in cui la codifica deve corrispondere, bit per bit, che è adatto per campi che sono dati binari realmente codificati (tra cui, ad esempio, Base64). L'ordinamento con distinzione tra maiuscole e minuscole porta a strani risultati e il confronto con distinzione tra maiuscole e minuscole può comportare che i valori duplicati differiscano solo nel caso di lettere, quindi le fascicoli con distinzione tra maiuscole e minuscole non sono più utili per i dati testuali - se il caso è significativo per te, allora punteggiatura altrimenti ignorabile e così via è probabilmente anche significativo, e un confronto binario potrebbe essere più appropriato.

Successivamente, unicodeo generalfa riferimento alle specifiche regole di ordinamento e confronto, in particolare il modo in cui il testo viene normalizzato o confrontato. Esistono molte diverse serie di regole per la codifica dei caratteri utf8mb4, con unicodeed generalessendo due che tentano di funzionare bene in tutte le lingue possibili anziché in una specifica. Le differenze tra queste due serie di regole sono oggetto di questa risposta. Si noti che unicodeutilizza le regole di Unicode 4.0. Le versioni recenti di MySQL aggiungono i set di regole unicode_520usando le regole di Unicode 5.2 e 0900(eliminando la parte "unicode_") usando le regole di Unicode 9.0.

E, infine, utf8mb4è ovviamente la codifica dei caratteri utilizzata internamente. In questa risposta sto parlando solo di codifiche basate su Unicode.


218
@KahWeeTeng Non dovresti mai, mai usare utf8_general_ci: semplicemente non funziona. È un ritorno ai brutti vecchi tempi della stooopeeedity ASCII di cinquant'anni fa. La corrispondenza senza maiuscole / minuscole Unicode non può essere eseguita senza la mappa a scomparsa dall'UDC. Ad esempio, "Σίσυφος" contiene tre diversi sigmi; o come la minuscola di "TSCHüẞ" è "tschüβ", ma la maiuscola di "tschüβ" è "TSCHÜSS". Puoi avere ragione o puoi essere veloce. Pertanto è necessario utilizzare utf8_unicode_ci, perché se non ti interessa la correttezza, è banale renderlo infinitamente veloce.
tchrist,

7
Dopo aver letto questo ho anche scoperto che utf8_unicode_ci considererà tutti i personaggi con lo stesso peso di confronto uguale per scopi di confronto di uguaglianza. Questo porta a casi in cui "か" == "が"o "ǽ" == "æ". Per l'ordinamento questo ha senso, ma potrebbe essere sorprendente quando si seleziona tramite uguaglianze o si tratta di indici unici - bugs.mysql.com/bug.php?id=16526
Mat Schaffer

4
@DanHorvat L'unico motivo pratico per limitarti al sottoinsieme più vecchio e più limitato di Unicode di Unicode è se hai una vecchia versione di MySQL che non supporta l'utf8mb4 più completo. 5.5.3 ha più di 5 anni. Mi rendo conto che Plesk viene eseguito su un programma MySQL diverso, ma la maggior parte delle distribuzioni sono su MySQL 5.5 ora e Plesk 11.x lo fa il supporto per MySQL 5.5 se si aggiorna i suoi componenti.
thomasrutter,

22
Non sarei d'accordo sul fatto che l'utilizzo della variante più recente, più standard di denuncia sia una cattiva pratica, e penso che sia infiammatorio chiamare le persone cattivi sviluppatori per qualcosa del genere. Potresti anche notare che la mia risposta così com'è dice " nelle nuove versioni di MySQL usa utf8mb4, piuttosto che utf8", sottolineo la mia.
thomasrutter,

24
@DanHorvat utf8mb4è l'unica scelta corretta . Con utf8te sei bloccato in alcune varianti di UTF8 a solo 3 byte di MySQL che solo MySQL (e MariaDB) sanno cosa fare. Il resto del mondo utilizza UTF8, che può contenere fino a 4 byte per carattere . Gli sviluppatori MySQL hanno erroneamente chiamato la loro codifica homebrew utf8e per non interrompere la retrocompatibilità, ora devono fare riferimento al vero UTF8 come utf8mb4.
Stijn de Witt,

162

Volevo sapere qual è la differenza di prestazioni tra l'utilizzo di utf8_general_cie utf8_unicode_ci, ma non ho trovato alcun benchmark elencato su Internet, quindi ho deciso di creare da solo i benchmark.

Ho creato una tabella molto semplice con 500.000 righe:

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

Quindi l'ho riempito con dati casuali eseguendo questa procedura memorizzata:

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

Quindi ho creato le seguenti stored procedure per eseguire il benchmark semplice SELECT, SELECTcon LIKEe l'ordinamento ( 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;

Nelle procedure memorizzate sopra utf8_general_civiene utilizzato il confronto, ma ovviamente durante i test ho usato sia utf8_general_cie utf8_unicode_ci.

Ho chiamato ogni procedura memorizzata 5 volte per ogni confronto (5 volte per utf8_general_cie 5 volte per utf8_unicode_ci) e quindi ho calcolato i valori medi.

I miei risultati sono:

benchmark_simple_select()

  • con utf8_general_ci: 9.957 ms
  • con utf8_unicode_ci: 10.271 ms

In questo benchmark l'utilizzo utf8_unicode_ciè più lento del utf8_general_ci3,2%.

benchmark_select_like()

  • con utf8_general_ci: 11.441 ms
  • con utf8_unicode_ci: 12.811 ms

In questo benchmark l'utilizzo utf8_unicode_ciè più lento del utf8_general_ci12%.

benchmark_order_by()

  • con utf8_general_ci: 11.944 ms
  • con utf8_unicode_ci: 12.887 ms

In questo benchmark l'utilizzo utf8_unicode_ciè più lento del utf8_general_ci7,9%.


16
Bel punto di riferimento, grazie per la condivisione. Sto ottenendo cifre sensibilmente simili (MySQL v5.6.12 su Windows): 10%, 4%, 8%. Sono d'accordo: il guadagno in termini di prestazioni di utf8_general_ciè semplicemente troppo minimo per essere utile.
RandomSeed,

10
1) Ma questo benchmark non dovrebbe generare risultati simili per le due regole di confronto per definizione? Voglio dire CONV(FLOOR(RAND() * 99999999999999), 20, 36)genera solo ASCII e nessun carattere Unicode che deve essere elaborato dagli algoritmi delle regole di confronto. 2) Description = 'test' COLLATE ...ed Description LIKE 'test%' COLLATE ...elaborare solo una singola stringa ("test") in fase di esecuzione, no? 3) Nelle app reali, le colonne utilizzate nell'ordinamento verrebbero probabilmente indicizzate e la velocità di indicizzazione su diverse regole di confronto con testo reale non ASCII potrebbe differire.
Halil Özgür,

2
@ HalilÖzgür - il tuo punto è parzialmente sbagliato. Immagino che non si tratti del valore del punto di codice che si trova all'esterno di ASCII (che general_ci gestirà correttamente), ma di caratteristiche specifiche, come il trattamento delle umlaut scritte come "Uml ea ute" o alcune di queste sottigliezze.
Tomasz Gandor,

38

Questo post lo descrive molto bene.

In breve: utf8_unicode_ci utilizza l'algoritmo di confronto Unicode come definito negli standard Unicode, mentre utf8_general_ci è un ordinamento più semplice che si traduce in risultati di ordinamento "meno accurati".


1
Grazie. quella era la mia impressione. prenderò il colpo di prestazione :)
onassar

7
Se non ti interessa la correttezza, allora è banale rendere qualsiasi algoritmo infinitamente veloce. Usa utf8_unicode_cie fai finta che l'altro non esista.
tchrist,

1
@tchrist, ma se ti interessa un certo equilibrio tra correttezza e velocità, utf8_general_cipotrebbe essere per te
Shelvacu,

@tchrist Non diventare mai un programmatore di giochi;)
Stijn de Witt il

1
@onassar - MySQL 8.0 afferma di aver migliorato significativamente le prestazioni di tutte le regole di confronto.
Rick James,

9

Consulta il manuale mysql, sezione Set di caratteri Unicode :

Per qualsiasi set di caratteri Unicode, le operazioni eseguite utilizzando le regole di confronto _general_ci sono più veloci di quelle per le regole di confronto _unicode_ci. Ad esempio, i confronti per la collazione utf8_general_ci sono più veloci, ma leggermente meno corretti, rispetto ai confronti per utf8_unicode_ci. La ragione di ciò è che utf8_unicode_ci supporta mappature come espansioni; cioè, quando un personaggio si confronta come uguale alle combinazioni di altri personaggi. Ad esempio, in tedesco e in altre lingue "ß" è uguale a "ss". utf8_unicode_ci supporta anche contrazioni e personaggi ignorabili. utf8_general_ci è una raccolta legacy che non supporta espansioni, contrazioni o personaggi ignorabili. Può fare solo confronti uno a uno tra i personaggi.

Quindi, per riassumere, utf_general_ci utilizza una serie di confronti più piccola e meno corretta (secondo lo standard) rispetto a utf_unicode_ci che dovrebbe implementare l'intero standard. Il set general_ci sarà più veloce perché c'è meno calcolo da fare.


18
Non esiste una cosa "leggermente meno corretta". La correttezza è una caratteristica booleana; non ammette modificatori di grado. Basta usare utf8_unicode_cie fingere che la versione rotta non sia presente.
tchrist

2
Ho avuto problemi a ottenere 5.6.15 per prendere l'impostazione collation_connection, e risulta che devi passarlo nella riga SET come 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'. Il merito va a Mathias Bynens per la soluzione, ecco la sua guida molto utile: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert,

4
@tchrist Il problema di dire che la correttezza è booleana è che non tiene conto delle situazioni che non si basano sulla correttezza assoluta. Il punto sottostante non è valido né sto tentando di sposare i vantaggi di general_ci, ma la tua affermazione generale sulla correttezza è facilmente confutata. Lo faccio quotidianamente nella mia professione. Commedia a parte, Stuart ha un buon punto qui .
Anthony,

5
Con la geolocalizzazione o lo sviluppo di giochi scambiamo sempre correttezza e prestazioni. E naturalmente la correttezza è un numero reale tra 0e 1, non un bool. :) Ad esempio, la selezione di punti geografici in un riquadro di delimitazione è un'approssimazione di "punti vicini" che non è buona come il calcolo della distanza tra il punto e il punto di riferimento e il filtraggio su quello. Ma entrambi sono un'approssimazione e, di fatto, non è possibile raggiungere la completa correttezza. Osserva il paradosso costiero e IEEE 754
Stijn de Witt,

4
TL; DR : Fornisci un programma che stampa il risultato corretto per1/3
Stijn de Witt,

7

In breve parole:

Se hai bisogno di un migliore ordinamento, usa utf8_unicode_ci(questo è il metodo preferito),

ma se sei totalmente interessato alle prestazioni, usa utf8_general_ci, ma sappi che è un po 'datato.

Le differenze in termini di prestazioni sono molto lievi.


1
Entrambi sono obsoleti ora - vedi risposta accettata per di più
thomasrutter

OK, grazie @thomasrutter
simhumileco,

6

Alcuni dettagli (PL)

Come possiamo leggere qui ( Peter Gulutzan ) c'è differenza nell'ordinare / confrontare la lettera polacca "Ł" (L con tratto - html esc:) Ł(minuscola: "ł" - html esc:) ł- abbiamo il seguente presupposto:

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

In lingua polacca la lettera Łè dopo lettera Le prima M. Nessuno di questi codici è migliore o peggiore: dipende dalle tue esigenze.


1

Ci sono due grandi differenze nell'ordinamento e nella corrispondenza dei caratteri:

Ordinamento :

  • utf8mb4_general_ci rimuove tutti gli accenti e ordina uno per uno che potrebbero creare risultati di ordinamento errati.
  • utf8mb4_unicode_ci ordinamento accurato.

Corrispondenza del personaggio

Abbinano i personaggi in modo diverso.

Ad esempio, utf8mb4_unicode_cihai i != ı, ma utf8mb4_general_cicontiene ı=i.

Ad esempio, immagina di avere una riga con name="Yılmaz". Poi

select id from users where name='Yilmaz';

restituirebbe la riga se la collocazione è utf8mb4_general_ci, ma se fosse collocata con utf8mb4_unicode_ciessa non restituirebbe la riga!

D'altra parte abbiamo quello a=ªe ß=ssin utf8mb4_unicode_cicui non è il caso utf8mb4_general_ci. Quindi immagina di avere una fila con name="ªßi", quindi

select id from users where name='assi';

restituirebbe la riga se la collocazione è utf8mb4_unicode_ci, ma non restituirebbe una riga se la collocazione è impostata su utf8mb4_general_ci.

Un elenco completo di corrispondenze per ogni collocazione può essere trovato qui .


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.