Яка різниця між utf8_general_ci та utf8_unicode_ci?


1063

Між utf8_general_ciта utf8_unicode_ci, чи є якісь відмінності щодо продуктивності?



6
Якщо вам подобається utf8[mb4]_unicode_ci, вам може сподобатися utf8[mb4]_unicode_520_ciще більше.
Рік Джеймс

8
Я не знаю, як я ставлюсь до цього - замість того, щоб фіксувати їх виконання відповідно до останнього стандарту Unicode, вони зберігають застарілу версію як за замовчуванням, і люди повинні додати "520", щоб використовувати належну її зараз. І це не сумісно вперед та назад, оскільки ви не можете використовувати версію "520" для старих версій MySQL. Чому вони не могли лише оновити існуюче порівняння? Те саме з "mb4", справді. Який код насправді залежав від старої, обмеженої / застарілої поведінки, щоб виправдати збереження цього за замовчуванням?
thomasrutter

7
Ще краще 8,0 за замовчуванням utf8mb4_0900_ai_ci.
Рік Джеймс

Відповіді:


1591

Ці два порівняння обидва для кодування символів UTF-8. Відмінності полягають у тому, як сортується та порівнюється текст.

Примітка. У MySQL потрібно використовувати, utf8mb4а не використовувати utf8. Конфузно utf8- хибна реалізація UTF-8 з ранніх версій MySQL, яка залишається лише для зворотної сумісності. Фіксована версія отримала назву utf8mb4.

Примітка: Новіші версії MySQL оновили правила сортування Unicode, доступні під такими іменами, як utf8mb4_0900_ai_ci для еквівалентних правил, заснованих на Unicode 9.0 - і без еквівалентного _general варіанту. Люди, які зараз це читають, ймовірно, повинні використовувати одне з цих нових порівнянь замість того _unicode чи іншого _general . Багато з того, що написано нижче, вже не викликає особливого інтересу, якщо ви можете замість цього використовувати одне з нових посилань.

Основні відмінності

  • utf8mb4_unicode_ci ґрунтується на офіційних правилах Unicode для універсального сортування та порівняння, які точно сортують у широкому діапазоні мов.

  • utf8mb4_general_ciце спрощений набір правил сортування, який має на меті зробити так само добре, як це може зробити, використовуючи безліч скорочень, призначених для підвищення швидкості. Це не дотримується правил Unicode і призведе до небажаного сортування чи порівняння в деяких ситуаціях, наприклад, при використанні певних мов чи символів.

    На сучасних серверах це підвищення продуктивності буде майже несуттєвим. Він був розроблений в той час, коли сервери мали невелику частину продуктивності процесора сучасних комп'ютерів.

Переваги utf8mb4_unicode_ciпонадutf8mb4_general_ci

utf8mb4_unicode_ci, що використовує правила Unicode для сортування та порівняння, використовує досить складний алгоритм правильного сортування на широкому діапазоні мов та при використанні широкого спектру спеціальних символів. Ці правила повинні враховувати конкретні мови; не всі сортують своїх персонажів у тому, що ми би назвали «алфавітним порядком».

Що стосується латинських (тобто "європейських") мов, то між сортуванням Unicode та спрощеним utf8mb4_general_ciсортуванням у MySQL немає великої різниці , але все ж є кілька відмінностей:

  • Наприклад, порівняння Unicode сортує "ß" як "ss" та "Œ", як "OE", як зазвичай хочуть користуватися цими символами, тоді як utf8mb4_general_ciсортує їх як окремі символи (імовірно, як "s" та "e" відповідно) .

  • Деякі символи Unicode визначені як ігнорувані, а це означає, що вони не повинні зараховуватися до порядку сортування, а порівняння має переходити до наступного символу. utf8mb4_unicode_ciсправляється з ними належним чином.

У не латинських мовах, таких як азіатські мови або мови з різними алфавітами, може бути набагато більше відмінностей між сортуванням Unicode та спрощеним utf8mb4_general_ciсортуванням. Придатність utf8mb4_general_ciбуде сильно залежати від мови, що використовується. Для деяких мов це буде зовсім неадекватно.

Що слід використовувати?

Майже напевно немає причин використовувати utf8mb4_general_ciбільше, тому що ми залишили позаду точку, коли швидкість процесора досить низька, щоб різниця в продуктивності була важливою. Ваша база даних майже напевно буде обмежена іншими вузькими місцями, ніж це.

Раніше деякі люди рекомендували використовувати, utf8mb4_general_ciза винятком випадків, коли точне сортування буде досить важливим, щоб виправдати вартість продуктивності. Сьогодні ці витрати на продуктивність майже не зникли, і розробники ставляться до інтернаціоналізації більш серйозно.

Слід аргументувати, що якщо швидкість для вас важливіша, ніж точність, ви також можете взагалі не робити сортування. Тривіально зробити алгоритм швидше, якщо він вам не потрібен, щоб бути точним. Отже, utf8mb4_general_ciце компроміс, який, мабуть, не потрібен з міркувань швидкості і, мабуть, також не підходить з міркувань точності.

Ще одне, що я додам, це те, що навіть якщо ви знаєте, що ваша програма підтримує лише англійську мову, можливо, все ж доведеться мати справу з іменами людей, які часто можуть містити символи, які використовуються іншими мовами, на яких так само важливо правильно сортувати. . Використання правил Unicode у всьому допомагає додати душевного спокою, що дуже розумні люди Unicode дуже наполегливо працювали, щоб сортування працювало належним чином.

Що означають деталі

По-перше, ciце для нечутливого до випадку сортування та порівняння. Це означає, що він підходить для текстових даних, а регістр не важливий. Інші типи зіставлення є cs(залежно від регістру) для текстових даних, коли важливий регістр, і bin, коли кодування має відповідати, біт для бітів, який підходить для полів, які дійсно закодовані двійкові дані (включаючи, наприклад, База64). Впорядкування залежно від регістру призводить до деяких дивних результатів, а порівняння з урахуванням регістру може призвести до того, що дублікати значень відрізняються лише в регістрі букв, тому порівняння з урахуванням регістру випадає з користі для текстових даних - якщо випадок має значення для вас, то в іншому випадку ігнорування пунктуації і так далі, ймовірно, також є значним, і двійкове порівняння може бути більш доречним.

Далі, unicodeабо generalпосилається на конкретні правила сортування та порівняння - зокрема, спосіб нормалізації чи порівняння тексту. Є багато різних наборів правил для кодування символів utf8mb4, з unicodeі generalбути два , які намагаються добре працювати у всіх можливих мовах , а не один конкретний один. Різниці між цими двома наборами правил є предметом цієї відповіді. Зверніть увагу, що unicodeвикористовуються правила Unicode 4.0. Останні версії MySQL додають набори правил, unicode_520використовуючи правила з Unicode 5.2, та 0900(скидаючи частину "unicode_"), використовуючи правила з Unicode 9.0.

І нарешті, utf8mb4звичайно, кодування символів, яке використовується всередині. У цій відповіді я говорю лише про кодування на основі Unicode.


218
@KahWeeTeng Ви повинні ніколи, ніколи НЕ використовувати utf8_general_ci: вона просто не працює. Це повернення до поганих старих часів ASCII stooopeeedity з п'ятдесяти років тому. Невідмінна відповідність регістру Unicode неможливо здійснити без складання карти складок з UCD. Наприклад, "Σίσυφος" має три різні сигми в ньому; або як нижній регістр "TSCHüẞ" є "tschüβ", а верхній регістр "tschüβ" - "TSCHÜSS". Ви можете мати рацію, або ви можете бути швидкими. Тому ви повинні користуватися utf8_unicode_ci, тому що якщо ви не піклуєтесь про правильність, то це неважливо, щоб зробити це нескінченно швидко.
tchrist

7
Прочитавши це, я також виявив, що utf8_unicode_ci вважатиме будь-які символи з однаковою вагою порівняння рівними для порівняння рівності. Це призводить до випадків, коли "か" == "が"або "ǽ" == "æ". Для сортування це має сенс, але може бути дивно, якщо вибирати через рівності або мати справу з унікальними індексами - bugs.mysql.com/bug.php?id=16526
Mat Schaffer

4
@DanHorvat Єдиною практичною причиною обмежити себе старшою, більш обмеженою підмножиною Unicode MySQL є те, що у вас є стара версія MySQL, яка не підтримує більш повну utf8mb4. 5.5.3 старше 5 років. Я розумію , що Plesk працює по іншому розкладом MySQL, але більшість дистрибутивів на MySQL 5.5 зараз і Plesk 11.x робить підтримку MySQL 5.5 , якщо її компоненти.
thomasrutter

22
Я не погоджуюся з тим, що використання нових, більш стандартних варіантів скарг є поганою практикою, і я вважаю, що називати людей поганими розробниками через щось подібне запально. Ви також можете зауважити, що моя відповідь, як це каже, " у нових версіях MySQL використовує utf8mb4, а не utf8", моє наголос.
thomasrutter

23
@DanHorvat utf8mb4- єдиний правильний вибір . З utf8вами застрягли деякі 3-байтові варіанти UTF8 лише для MySQL, про які тільки MySQL (і MariaDB) знають, що робити. Решта світу використовує UTF8, який може містити до 4 байт на символ . Розробники MySQL неправильно назвали кодування домашньою мовою, utf8і щоб не порушити зворотну сумісність, тепер вони повинні посилатися на реальний UTF8 як utf8mb4.
Штійн де Вітт

162

Мені хотілося знати, в чому полягає різниця в роботі між використанням utf8_general_ciта utf8_unicode_ci, але я не знайшов жодних орієнтирів, перелічених в Інтернеті, тому вирішив створити орієнтири самостійно.

Я створив дуже просту таблицю з 500 000 рядків:

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

Потім я заповнив їх випадковими даними, запустивши цю збережену процедуру:

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

Тоді я створив наступні збережені процедури для порівняння простого SELECT, SELECTз LIKEі сортування ( SELECTз 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;

У збережених вище процедурах utf8_general_ciвикористовується порівняння, але, звичайно, під час тестів я використовував і те, utf8_general_ciі utf8_unicode_ci.

Я називав кожну збережену процедуру 5 разів за кожне порівняння (5 разів для utf8_general_ciта 5 разів для utf8_unicode_ci), а потім обчислював середні значення.

Мої результати такі:

benchmark_simple_select()

  • з utf8_general_ci: 9957 мс
  • з utf8_unicode_ci: 10 271 мс

У цьому орієнтирі використання utf8_unicode_ciвідбувається повільніше, ніж utf8_general_ciна 3,2%.

benchmark_select_like()

  • з utf8_general_ci: 11,441 мс
  • з utf8_unicode_ci: 12,811 мс

У цьому орієнтирі використання utf8_unicode_ciповільніше, ніж utf8_general_ciна 12%.

benchmark_order_by()

  • з utf8_general_ci: 11 944 мс
  • з utf8_unicode_ci: 12,887 мс

У цьому еталоні використання використовується utf8_unicode_ciповільніше, ніж utf8_general_ciна 7,9%.


16
Хороший орієнтир, дякую за обмін. Я отримую подібні показники (MySQL v5.6.12 для Windows): 10%, 4%, 8%. Я погоджуюся: підвищення продуктивності utf8_general_ciпросто занадто мінімальне, щоб його варто використовувати.
RandomSeed

10
1) Чи не повинен цей показник генерувати подібні результати для двох порівнянь за визначенням? Я маю на увазі, CONV(FLOOR(RAND() * 99999999999999), 20, 36)генерує лише ASCII, і ніякі символи Unicode не обробляються алгоритмами порівнянь. 2) Description = 'test' COLLATE ...і Description LIKE 'test%' COLLATE ...обробляти лише один рядок ("тест") під час виконання, чи не так? 3) У реальних додатках стовпці, що використовуються для впорядкування, ймовірно, будуть проіндексовані, а швидкість індексації у різних зіставленнях з реальним текстом, що не належить до ASCII, може відрізнятися.
Halil Özgür

2
@ HalilÖzgür - ваша думка частково неправильна. Напевно, справа не в тому, що значення кодової точки знаходиться поза ASCII (з якою генеральний_ci би правильно поводився), а в конкретних особливостях, як, наприклад, обробка umlauts, написаних як "Uml ea ute", або деяких подібних тонкощів.
Томаш Гандор

38

Цей пост дуже добре описує це.

Якщо коротко: utf8_unicode_ci використовує алгоритм зібрання Unicode, як визначено у стандартах Unicode, тоді як utf8_general_ci - більш простий порядок сортування, що призводить до "менш точних" результатів сортування.


1
Дякую. таке було моє враження. я
прийму

7
Якщо ви не піклуєтесь про правильність, тоді будь-який алгоритм нескінченно швидкий. Просто використовуйте utf8_unicode_ciі робіть вигляд, що іншого не існує.
tchrist

1
@tchrist, але якщо ви дбаєте про певний баланс між правильністю та швидкістю, utf8_general_ciможливо, для вас
Шельваку,

@tchrist Ніколи не станьте ігровим програмістом;)
Stijn de Witt

1
@onassar - MySQL 8.0 стверджує, що значно покращив продуктивність усіх посилань.
Рік Джеймс

9

Дивіться посібник з mysql, розділ Набори символів Unicode :

Для будь-якого набору символів Unicode операції, виконані за допомогою зіставлення _general_ci, є швидшими, ніж операції для порівняння _unicode_ci. Наприклад, порівняння для порівняння utf8_general_ci є швидшим, але трохи менш правильним, ніж порівняння для utf8_unicode_ci. Причиною цього є те, що utf8_unicode_ci підтримує відображення, наприклад розширення; тобто коли один символ порівнюється як рівний комбінаціям інших символів. Наприклад, у німецькій та деяких інших мовах "ß" дорівнює "ss". utf8_unicode_ci також підтримує скорочення та ігноровані символи. utf8_general_ci - це застаріле зіставлення, яке не підтримує розширення, скорочення чи ігноровані символи. Він може лише порівнювати персонажів один на один.

Отже, підводячи підсумок, utf_general_ci використовує менший і менш правильний (відповідно до стандарту) набір порівнянь, ніж utf_unicode_ci, який повинен реалізувати весь стандарт. Набір General_ci буде швидшим, тому що обчислень буде менше.


18
Не існує такого поняття, як "трохи менш правильне". Правильність - булева характеристика; він не допускає модифікаторів ступеня. Просто використовуйте utf8_unicode_ciі робите вигляд, що помилкова версія не існує.
tchrist

2
У мене виникли проблеми з отриманням 5.6.15, щоб прийняти налаштування collation_connection, і виявиться, що вам потрібно передати його в рядку SET, наприклад, "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci". Заслуга Матіаса Байненса за рішення, ось його дуже корисний посібник: mathiasbynens.be/notes/mysql-utf8mb4
Стів Хібберт

4
@tchrist Проблема з твердженням коректності булева, оскільки вона не враховує ситуацій, які не покладаються на абсолютну коректність. Ваша основна точка не є недійсною, і я не намагаюся використовувати переваги General_ci, але ваше загальне твердження про правильність легко спростувати. Я роблю це щодня у своїй професії. Комедія в стороні, Стюарт має хорошу точку тут .
Ентоні

5
Завдяки геолокації чи розробці ігор ми постійно торгуємося правильністю та продуктивністю. І звичайно правильність - це дійсне число між, 0а 1не бул. :) Вибір геопотоків у обмежувальному полі - це наближення "точок поблизу", що не так добре, як обчислення відстані між точкою та опорною точкою та фільтрація по цьому. Але обидва - це наближення, і фактично повна коректність здебільшого не досяжна. Дивіться парадокс узбережжя та IEEE 754
Штійн де Вітт

4
TL; DR : Будь ласка, надайте програму, яка надрукує правильний результат для1/3
Stijn de Witt

7

Коротко кажучи:

Якщо вам потрібен кращий порядок сортування - використовуйте utf8_unicode_ci(це кращий метод),

але якщо вас дуже цікавить продуктивність - використовуйте utf8_general_ci, але знайте, що вона трохи застаріла.

Відмінності щодо продуктивності дуже незначні.


1
Обидва зараз застаріли - більше див. У прийнятій відповіді
thomasrutter

Гаразд, дякую @thomasrutter
simhumileco

6

Деякі деталі (PL)

Як ми можемо прочитати тут ( Пітер Гулутзан ), існує різниця в сортуванні / порівнянні польської літери "Ł" (L з обведенням - html esc:) Ł(нижній регістр: "ł" - html esc:) ł- ми маємо таке припущення:

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

У польській мові письмо Łйде після письма Lі раніше M. Ніхто з цього кодування не є кращим чи гіршим - це залежить від ваших потреб.


1

Існує дві великі різниці в сортуванні та відповідності символів:

Сортування :

  • utf8mb4_general_ci видаляє всі наголоси та сортує по черзі, що може створити неправильні результати сортування.
  • utf8mb4_unicode_ci сортує точно.

Відповідність персонажів

Вони по-різному відповідають персонажам.

Наприклад, у utf8mb4_unicode_ciвас є i != ı, але в utf8mb4_general_ciньому тримається ı=i.

Наприклад, уявіть, що у вас є сварки name="Yılmaz". Тоді

select id from users where name='Yilmaz';

повертає рядок, якщо колокація є utf8mb4_general_ci, але якщо вона буде розміщена, utf8mb4_unicode_ciвона не поверне рядок!

З іншого боку , ми маємо , що a=ªі ß=ssв utf8mb4_unicode_ciяких не буває в utf8mb4_general_ci. Отже , уявіть , у вас є рядок з name="ªßi", то

select id from users where name='assi';

повертає рядок, якщо колокація є utf8mb4_unicode_ci, але не повертає рядок, якщо встановлено колокацію utf8mb4_general_ci.

Повний список відповідностей для кожної колокації можна знайти тут .


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.