Tra utf8_general_ci
e utf8_unicode_ci
, ci sono differenze in termini di prestazioni?
utf8[mb4]_unicode_ci
, ti potrebbe piacere utf8[mb4]_unicode_520_ci
anche di più.
utf8mb4_0900_ai_ci
.
Tra utf8_general_ci
e utf8_unicode_ci
, ci sono differenze in termini di prestazioni?
utf8[mb4]_unicode_ci
, ti potrebbe piacere utf8[mb4]_unicode_520_ci
anche di più.
utf8mb4_0900_ai_ci
.
Risposte:
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 utf8mb4
piuttosto 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_ci
oltreutf8mb4_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_ci
ordinamento 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_ci
ordinano 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_ci
gestisce 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_ci
ordinamento semplificato . L'idoneità utf8mb4_general_ci
dipenderà 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_ci
tranne 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, unicode
o general
fa 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 unicode
ed general
essendo 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 unicode
utilizza le regole di Unicode 4.0. Le versioni recenti di MySQL aggiungono i set di regole unicode_520
usando 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.
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.
"か" == "が"
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
utf8mb4
è l'unica scelta corretta . Con utf8
te 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 utf8
e per non interrompere la retrocompatibilità, ora devono fare riferimento al vero UTF8 come utf8mb4
.
Volevo sapere qual è la differenza di prestazioni tra l'utilizzo di utf8_general_ci
e 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
, SELECT
con LIKE
e l'ordinamento ( SELECT
con 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_ci
viene utilizzato il confronto, ma ovviamente durante i test ho usato sia utf8_general_ci
e utf8_unicode_ci
.
Ho chiamato ogni procedura memorizzata 5 volte per ogni confronto (5 volte per utf8_general_ci
e 5 volte per utf8_unicode_ci
) e quindi ho calcolato i valori medi.
I miei risultati sono:
benchmark_simple_select()
utf8_general_ci
: 9.957 ms utf8_unicode_ci
: 10.271 ms In questo benchmark l'utilizzo utf8_unicode_ci
è più lento del utf8_general_ci
3,2%.
benchmark_select_like()
utf8_general_ci
: 11.441 ms utf8_unicode_ci
: 12.811 ms In questo benchmark l'utilizzo utf8_unicode_ci
è più lento del utf8_general_ci
12%.
benchmark_order_by()
utf8_general_ci
: 11.944 ms utf8_unicode_ci
: 12.887 ms In questo benchmark l'utilizzo utf8_unicode_ci
è più lento del utf8_general_ci
7,9%.
utf8_general_ci
è semplicemente troppo minimo per essere utile.
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.
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".
utf8_unicode_ci
e fai finta che l'altro non esista.
utf8_general_ci
potrebbe essere per te
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.
utf8_unicode_ci
e fingere che la versione rotta non sia presente.
0
e 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
1/3
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.
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 L
e prima M
. Nessuno di questi codici è migliore o peggiore: dipende dalle tue esigenze.
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_ci
hai i != ı
, ma utf8mb4_general_ci
contiene ı=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_ci
essa non restituirebbe la riga!
D'altra parte abbiamo quello a=ª
e ß=ss
in utf8mb4_unicode_ci
cui 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 .
Secondo questo post, c'è un notevole vantaggio in termini di prestazioni su MySQL 5.7 quando si utilizza utf8mb4_general_ci al posto di utf8mb4_unicode_ci: https://www.percona.com/blog/2019/02/27/charset-and-collation-settings-impact -on-mysql-prestazioni /