Jaka jest różnica między utf8_general_ci i utf8_unicode_ci?

1063

Pomiędzy utf8_general_cii utf8_unicode_ciczy są jakieś różnice pod względem wydajności?

KahWee Teng
źródło
1
Zobacz również stackoverflow.com/questions/1036454/...
Unor
6
Jeśli chcesz utf8[mb4]_unicode_ci, to może jak utf8[mb4]_unicode_520_cijeszcze bardziej.
Rick James
8
Nie wiem, jak się z tym czuję - zamiast naprawiać ich implementację, aby była zgodna z najnowszym standardem Unicode, zachowują przestarzałą wersję jako domyślną i ludzie muszą dodać „520”, aby użyć teraz właściwej. I nie jest kompatybilny z poprzednimi i starszymi wersjami, ponieważ nie można używać wersji „520” na starszych wersjach MySQL. Dlaczego nie mogli właśnie zaktualizować swojego istniejącego zestawu? To samo z „mb4”, naprawdę. Jaki kod naprawdę zależał od starego, ograniczonego / przestarzałego zachowania, aby uzasadnić zachowanie go jako domyślnego?
thomasrutter
7
Jeszcze lepsza jest domyślna wersja 8.0 utf8mb4_0900_ai_ci.
Rick James

Odpowiedzi:

1591

Te dwa zestawienia dotyczą kodowania znaków UTF-8. Różnice polegają na sposobie sortowania i porównywania tekstu.

Uwaga: W MySQL musisz utf8mb4raczej używać niż utf8. Myląco utf8jest wadliwą implementacją UTF-8 z wczesnych wersji MySQL, która pozostaje tylko dla kompatybilności wstecznej. Naprawiona wersja otrzymała nazwę utf8mb4.

Uwaga: Nowsze wersje MySQL mają zaktualizowane reguły sortowania Unicode, dostępne pod nazwami, na przykład utf8mb4_0900_ai_ci dla równoważnych reguł opartych na Unicode 9.0 - i bez równoważnego _general wariantu. Osoby czytające to teraz powinny prawdopodobnie użyć jednego z tych nowszych zestawień zamiast jednego _unicode lub _general . Wiele z tego, co napisano poniżej, nie jest już tak interesujące, jeśli zamiast tego możesz użyć jednego z nowszych zestawień.

Kluczowe różnice

  • utf8mb4_unicode_ci opiera się na oficjalnych regułach Unicode dotyczących uniwersalnego sortowania i porównywania, które dokładnie sortują w szerokim zakresie języków.

  • utf8mb4_general_cito uproszczony zestaw reguł sortowania, który ma na celu jak najlepiej wykonać, przy jednoczesnym zastosowaniu wielu skrótów mających na celu poprawę prędkości. Nie jest zgodny z zasadami Unicode i spowoduje niepożądane sortowanie lub porównanie w niektórych sytuacjach, na przykład podczas używania określonych języków lub znaków.

    Na nowoczesnych serwerach ten wzrost wydajności będzie prawie znikomy. Został on opracowany w czasach, gdy serwery miały niewielki ułamek wydajności procesora dzisiejszych komputerów.

Korzyści z utf8mb4_unicode_ciponadutf8mb4_general_ci

utf8mb4_unicode_ci, który korzysta z reguł Unicode do sortowania i porównywania, stosuje dość złożony algorytm do poprawnego sortowania w szerokim zakresie języków i przy użyciu szerokiej gamy znaków specjalnych. Zasady te muszą uwzględniać konwencje specyficzne dla języka; nie wszyscy sortują swoje postacie w tak zwanym „porządku alfabetycznym”.

Jeśli chodzi o języki łacińskie (tj. „Europejskie”), nie ma dużej różnicy między sortowaniem Unicode a uproszczonym utf8mb4_general_cisortowaniem w MySQL, ale wciąż istnieje kilka różnic:

  • Na przykład sortowanie w Unicode sortuje „ß” jak „ss”, a „Œ” jak „OE” tak, jak ludzie używający tych znaków normalnie by tego chcieli, a utf8mb4_general_cisortuje je jako pojedyncze znaki (przypuszczalnie odpowiednio „s” i „e”) .

  • Niektóre znaki Unicode są zdefiniowane jako ignorowalne, co oznacza, że ​​nie powinny się liczyć w kolejności sortowania, a porównanie powinno przejść do następnego znaku. utf8mb4_unicode_ciradzi sobie z nimi poprawnie.

W językach innych niż łacińskie, takich jak języki azjatyckie lub języki z różnymi alfabetami, może występować znacznie więcej różnic między sortowaniem w Unicode a utf8mb4_general_cisortowaniem uproszczonym . Przydatność utf8mb4_general_cizależy w dużej mierze od użytego języka. W przypadku niektórych języków będzie to dość nieodpowiednie.

Czego powinieneś użyć?

Niemal na pewno nie ma już powodu, aby z niego korzystać utf8mb4_general_ci, ponieważ pozostawiliśmy za sobą punkt, w którym szybkość procesora jest na tyle niska, że ​​różnica w wydajności byłaby ważna. Twoja baza danych prawie na pewno będzie ograniczona innymi wąskimi gardłami.

W przeszłości niektóre osoby zalecały stosowanie, utf8mb4_general_ciz wyjątkiem przypadków, gdy dokładne sortowanie miało być na tyle ważne, aby uzasadnić koszt wydajności. Dzisiaj ten koszt wydajności prawie zniknął, a programiści poważniej podchodzą do internacjonalizacji.

Trzeba wysunąć argument, że jeśli prędkość jest dla Ciebie ważniejsza niż dokładność, równie dobrze możesz w ogóle nie dokonywać sortowania. Usprawnienie algorytmu jest trywialne, jeśli nie jest potrzebny, aby był dokładny. Tak, utf8mb4_general_cijest to kompromis, który chyba nie potrzebne ze względów prędkości i prawdopodobnie również nie nadaje się ze względu na dokładność.

Dodam jeszcze jedną rzecz, że nawet jeśli wiesz, że aplikacja obsługuje tylko język angielski, może nadal wymagać rozpoznawania nazwisk osób, które często mogą zawierać znaki używane w innych językach, w których równie ważne jest prawidłowe sortowanie . Używanie reguł Unicode do wszystkiego pomaga dodać spokój, że bardzo inteligentni ludzie Unicode pracowali bardzo ciężko, aby sortowanie działało poprawnie.

Co oznaczają części

Po pierwsze, cisłuży do sortowania i porównywania bez rozróżniania wielkości liter . Oznacza to, że nadaje się do danych tekstowych, a wielkość liter nie ma znaczenia. Inne typy zestawiania są cs(z rozróżnianiem wielkości liter) dla danych tekstowych bin, w których wielkość liter jest ważna, i tam , gdzie kodowanie musi się zgadzać, bit po bicie, co jest odpowiednie dla pól, które są naprawdę zakodowanymi danymi binarnymi (w tym na przykład Base64). Sortowanie z rozróżnianiem wielkości liter prowadzi do dziwnych wyników, a porównywanie z rozróżnianiem wielkości liter może powodować, że zduplikowane wartości różnią się tylko wielkością liter, więc sortowanie z rozróżnianiem wielkości liter nie jest korzystne dla danych tekstowych - jeśli wielkość liter jest dla Ciebie ważna, to w przeciwnym razie ignorowana interpunkcja i tak dalej jest prawdopodobnie również znaczący, a sortowanie binarne może być bardziej odpowiednie.

Dalej unicodelub generalodnosi się do konkretnych zasad sortowania i porównywania - w szczególności sposobu normalizacji lub porównania tekstu. Istnieje wiele różnych zestawów reguł dla kodowania znaków utf8mb4, ze unicodei generalbędąc dwa, że próba pracy dobrze we wszystkich możliwych językach niż jeden specyficzny. Różnice między tymi dwoma zestawami reguł są przedmiotem tej odpowiedzi. Zauważ, że unicodeużywa reguł z Unicode 4.0. Najnowsze wersje MySQL dodają zestawy reguł unicode_520przy użyciu reguł z Unicode 5.2 i 0900(upuszczając część „Unicode_”) przy użyciu reguł z Unicode 9.0.

I na koniec, utf8mb4oczywiście kodowanie znaków jest używane wewnętrznie. W tej odpowiedzi mówię tylko o kodowaniu opartym na Unicode.

thomasrutter
źródło
218
@KahWeeTeng Należy nigdy, nigdy używać utf8_general_ci: to po prostu nie działa. To powrót do starych, złych dni głupoty ASCII sprzed pięćdziesięciu lat. Dopasowywanie bez rozróżniania wielkości liter w Unicode nie może być wykonane bez mapy folderów z UCD. Na przykład „Σίσυφος” zawiera trzy różne sigmy; lub jak małymi literami „TSCHüẞ” jest „tschüβ”, ale wielką literą „tschüβ” jest „TSCHÜSS”. Możesz mieć rację lub możesz być szybki. Dlatego musisz go użyć utf8_unicode_ci, ponieważ jeśli nie zależy ci na poprawności, to sprawienie, by było nieskończenie szybkie, jest banalne.
tchrist
7
Po przeczytaniu tego odkryłem również, że utf8_unicode_ci weźmie pod uwagę wszystkie znaki o tej samej wadze sortowania jako równe dla celów porównania równości. Prowadzi to do przypadków, w których "か" == "が"lub "ǽ" == "æ". Sortowanie to ma sens, ale może być zaskakujące, gdy wybierasz
Mat Schaffer
4
@DanHorvat Jedynym praktycznym powodem, aby ograniczyć się do starszego, bardziej ograniczonego podzbioru MySQL, jest posiadanie starej wersji MySQL, która nie obsługuje bardziej kompletnego utf8mb4. 5.5.3 ma ponad 5 lat. Doceniam, że Plesk działa na innym harmonogramem MySQL, ale większość dystrybucje są na MySQL 5.5 i teraz Plesk 11.x robi obsługą MySQL 5.5 po aktualizacji jego składników.
thomasrutter,
22
Nie zgodziłbym się z tym, że używanie nowszego, bardziej zgodnego ze standardami wariantu, jest złą praktyką i myślę, że nałogowe jest nazywanie ludzi złymi programistami w związku z czymś takim. Warto również zauważyć, że moja obecna odpowiedź brzmi: „ w nowych wersjach MySQL używaj utf8mb4 zamiast utf8”, moje podkreślenie.
thomasrutter,
23
@ DanHorvat utf8mb4jest jedynym poprawnym wyborem . Gdy utf8utkniesz w jakimś MySQL, 3-bajtowym wariancie UTF8, który tylko MySQL (i MariaDB) wiedzą, co zrobić. Reszta świata używa UTF8, który może zawierać do 4 bajtów na znak . MySQL devs źle nazwali swoje kodowanie homebrew utf8i aby nie złamać kompatybilności wstecznej, muszą teraz odnosić się do prawdziwego UTF8 jako utf8mb4.
Stijn de Witt
162

Chciałem wiedzieć, jaka jest różnica w wydajności między używaniem utf8_general_cii utf8_unicode_ci, ale nie znalazłem żadnych testów porównawczych w Internecie, więc postanowiłem sam je stworzyć.

Stworzyłem bardzo prostą tabelę z 500 000 wierszy:

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

Następnie wypełniłem je losowymi danymi, uruchamiając tę ​​procedurę składowaną:

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

Następnie utworzyłem następujące procedury składowane, aby przeprowadzić proste testy porównawcze SELECT, SELECTz LIKEi sortowanie ( SELECTz 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;

W procedurach przechowywanych stosuje się powyższe utf8_general_cizestawienie, ale oczywiście podczas testów użyłem zarówno utf8_general_cii utf8_unicode_ci.

Każdą procedurę przechowywaną wywołałem 5 razy dla każdego zestawienia (5 razy dla utf8_general_cii 5 razy dla utf8_unicode_ci), a następnie obliczyłem wartości średnie.

Moje wyniki to:

benchmark_simple_select()

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

W tym teście używanie utf8_unicode_cijest wolniejsze niż utf8_general_cio 3,2%.

benchmark_select_like()

  • z utf8_general_ci: 11441 ms
  • z utf8_unicode_ci: 12,811 ms

W tym teście używanie utf8_unicode_cijest wolniejsze niż utf8_general_cio 12%.

benchmark_order_by()

  • z utf8_general_ci: 11944 ms
  • z utf8_unicode_ci: 12887 ms

W tym teście używanie utf8_unicode_cijest wolniejsze niż utf8_general_cio 7,9%.

nightcoder
źródło
16
Niezły test, dziękuję za udostępnienie. Otrzymuję rozsądnie podobne liczby (MySQL v5.6.12 w systemie Windows): 10%, 4%, 8%. Zgadzam się: wzrost wydajności utf8_general_cijest po prostu zbyt minimalny, aby być wartym użycia.
RandomSeed
10
1) Ale czy ten test porównawczy nie powinien generować podobnych wyników dla dwóch zestawień z definicji? To znaczy CONV(FLOOR(RAND() * 99999999999999), 20, 36)generuje tylko ASCII i nie ma znaków Unicode do przetworzenia przez algorytmy sortowania. 2) Description = 'test' COLLATE ...i Description LIKE 'test%' COLLATE ...przetwarzają tylko jeden ciąg („test”) w czasie wykonywania, prawda? 3) W rzeczywistych aplikacjach kolumny używane do porządkowania prawdopodobnie zostałyby zaindeksowane, a szybkość indeksowania dla różnych zestawień z prawdziwym tekstem innym niż ASCII może się różnić.
Halil Özgür
2
@ HalilÖzgür - twój punkt jest częściowo błędny. Wydaje mi się, że nie chodzi o to, aby wartość punktu kodowego znajdowała się poza ASCII (co general_ci poradziłaby sobie poprawnie), ale o specyficzne cechy, takie jak traktowanie umlautów napisanych jako „Uml ea ute” lub niektórych takich subtelności.
Tomasz Gandor
38

Ten post opisuje to bardzo ładnie.

W skrócie: utf8_unicode_ci używa algorytmu sortowania Unicode zdefiniowanego w standardach Unicode, podczas gdy utf8_general_ci jest prostszym porządkiem sortowania, co powoduje „mniej dokładne” wyniki sortowania.

Michael Madsen
źródło
1
dzięki. takie było moje wrażenie. wezmę hit wydajności :)
onassar
7
Jeśli nie zależy ci na poprawności, nie jest konieczne, aby każdy algorytm był nieskończenie szybki. Po prostu użyj utf8_unicode_cii udawaj, że ten drugi nie istnieje.
tchrist
1
@ tchrist, ale jeśli zależy ci na pewnej równowadze między poprawnością a szybkością, utf8_general_cimoże być dla ciebie
Shelvacu
@tchrist Nigdy nie zostań programistą gier;)
Stijn de Witt
1
@onassar - MySQL 8.0 twierdzi, że znacznie poprawił wydajność wszystkich zestawień.
Rick James
9

Zobacz instrukcję mysql, sekcja Zestawy znaków Unicode :

W przypadku dowolnego zestawu znaków Unicode operacje wykonywane przy użyciu sortowania _general_ci są szybsze niż w przypadku sortowania _unicode_ci. Na przykład porównania dla zestawienia utf8_general_ci są szybsze, ale nieco mniej poprawne, niż porównania dla utf8_unicode_ci. Powodem tego jest to, że utf8_unicode_ci obsługuje mapowania, takie jak rozszerzenia; to znaczy, gdy jeden znak porównuje się jako równy kombinacjom innych znaków. Na przykład w języku niemieckim i niektórych innych językach „ß” jest równe „ss”. utf8_unicode_ci obsługuje również skurcze i znaki ignorowalne. utf8_general_ci to starsze zestawienie, które nie obsługuje rozszerzeń, skurczów ani ignorowalnych znaków. Może dokonywać tylko porównań między postaciami.

Podsumowując, utf_general_ci używa mniejszego i mniej poprawnego (zgodnie ze standardem) zestawu porównań niż utf_unicode_ci, który powinien implementować cały standard. Zestaw general_ci będzie szybszy, ponieważ jest mniej obliczeń do zrobienia.

Dana Zdrowa
źródło
18
Nie ma czegoś takiego jak „nieco mniej poprawne”. Prawidłowość jest cechą logiczną; nie dopuszcza modyfikatorów stopnia. Po prostu użyj utf8_unicode_cii udawaj, że uszkodzona wersja z błędami nie istnieje.
tchrist
2
Miałem problemy z uzyskaniem 5.6.15 ustawienia collation_connection i okazuje się, że musisz przekazać go w linii SET, np. „SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci”. Podziękowania dla Mathiasa Bynensa za rozwiązanie, oto jego bardzo przydatny przewodnik: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert
4
@tchrist Problem z mówieniem o poprawności jest logiczny, ponieważ nie uwzględnia sytuacji, które nie polegają na absolutnej poprawności. Twój punkt bazowy nie jest nieważny, ani nie próbuję popierać korzyści wynikających z general_ci, ale twoje ogólne stwierdzenie o poprawności można łatwo obalić. Robię to codziennie w moim zawodzie. Pomijając komedię, Stuart ma tutaj rację .
Anthony
5
Dzięki geolokalizacji lub tworzeniu gier cały czas wymieniamy poprawność z wydajnością. I oczywiście poprawność to rzeczywista liczba między, 0a 1nie bool. :) EG wybranie punktów geograficznych w obwiedni jest przybliżeniem „punktów w pobliżu”, co nie jest tak dobre, jak obliczenie odległości między punktem a punktem odniesienia i filtrowanie tego. Ale oba są przybliżeniem i w rzeczywistości pełna poprawność jest w większości nieosiągalna. Zobacz paradoks wybrzeża i IEEE 754
Stijn de Witt
4
TL; DR : Proszę podać program, który drukuje poprawny wynik dla1/3
Stijn de Witt
7

Krótko mówiąc:

Jeśli potrzebujesz lepszego porządku sortowania - użyj utf8_unicode_ci(jest to preferowana metoda),

ale jeśli jesteś całkowicie zainteresowany wydajnością - użyj utf8_general_ci, ale wiedz, że jest trochę przestarzała.

Różnice pod względem wydajności są bardzo niewielkie.

simhumileco
źródło
1
Oba są już nieaktualne - więcej informacji znajduje się w zaakceptowanej odpowiedzi
thomasrutter
OK, dziękuję @ thomasrutter
simhumileco
6

Niektóre szczegóły (PL)

Jak możemy przeczytać tutaj ( Peter Gulutzan ), istnieje różnica w sortowaniu / porównywaniu polskiej litery „Ł” (L z pociągnięciem - html esc:) Ł(małe litery: „ł” - html esc:) ł- przyjmujemy następujące założenie:

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

W języku polskim litera Łjest po literze Li przed nią M. Żadne z tych kodów nie jest lepsze ani gorsze - zależy to od twoich potrzeb.

Kamil Kiełczewski
źródło
1

Istnieją dwie duże różnice w sortowaniu i dopasowywaniu znaków:

Sortowanie :

  • utf8mb4_general_ci usuwa wszystkie akcenty i sortuje jeden po drugim, co może powodować niepoprawne wyniki sortowania.
  • utf8mb4_unicode_ci sortuje dokładne.

Dopasowanie postaci

Pasują do znaków inaczej.

Na przykład w utf8mb4_unicode_citobie masz i != ı, ale w utf8mb4_general_cinim się trzyma ı=i.

Wyobraź sobie na przykład, że masz wiersz name="Yılmaz". Następnie

select id from users where name='Yilmaz';

zwróci wiersz, jeśli kolokacja jest utf8mb4_general_ci, ale jeśli zostanie skolokowany utf8mb4_unicode_ci, nie zwróci wiersza!

Z drugiej strony mamy, że a=ªi ß=ssw utf8mb4_unicode_ciktórej nie jest w przypadku utf8mb4_general_ci. Więc wyobraź sobie, że masz wiersz z name="ªßi", a następnie

select id from users where name='assi';

zwróci wiersz, jeśli kolokacja jest utf8mb4_unicode_ci, ale nie zwróci wiersza, jeśli kolokacja jest ustawiona na utf8mb4_general_ci.

Pełna lista dopasowań dla każdej kolokacji znajduje się tutaj .

Adam
źródło