В чем разница между utf8_general_ci и utf8_unicode_ci?

1063

Между utf8_general_ciи utf8_unicode_ciесть ли различия с точки зрения производительности?

KahWee Teng
источник
1
Смотрите также stackoverflow.com/questions/1036454/...
ОООНР
6
Если вам нравится utf8[mb4]_unicode_ci, вам может понравиться utf8[mb4]_unicode_520_ciеще больше.
Рик Джеймс
8
Я не знаю, что я чувствую по этому поводу - вместо того, чтобы привести их реализацию в соответствие с последним стандартом Unicode, они оставляют устаревшую версию по умолчанию, и люди должны добавить «520», чтобы использовать правильную версию сейчас. И он не поддерживает прямую и обратную совместимость, потому что вы не можете использовать версию «520» в старых версиях MySQL. Почему они не могли просто обновить существующие параметры сортировки? То же самое с "mb4", правда. Какой код действительно зависит от старого, ограниченного / устаревшего поведения, чтобы оправдать его сохранение по умолчанию?
Томасруттер
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Это упрощенный набор правил сортировки, цель которого - сделать все возможное, используя множество ярлыков, предназначенных для повышения скорости. Он не следует правилам Юникода и приведет к нежелательной сортировке или сравнению в некоторых ситуациях, например при использовании определенных языков или символов.

    На современных серверах это повышение производительности будет практически незначительным. Он был разработан в то время, когда серверы имели небольшую долю производительности ЦП современных компьютеров.

Преимущества utf8mb4_unicode_ciболееutf8mb4_general_ci

utf8mb4_unicode_ci, который использует правила Unicode для сортировки и сравнения, использует довольно сложный алгоритм для правильной сортировки в широком диапазоне языков и при использовании широкого диапазона специальных символов. Эти правила должны учитывать языковые соглашения; не каждый сортирует своих персонажей в том, что мы назвали бы «алфавитным порядком».

Что касается латиницы (то есть "европейских") языков, между сортировкой Unicode и упрощенной utf8mb4_general_ciсортировкой в ​​MySQL нет большой разницы , но есть еще несколько отличий:

  • Например, параметры сортировки Unicode сортируют «ß», например, «ss», и «Œ», например «OE», как обычно хотят люди, использующие эти символы, тогда как utf8mb4_general_ciсортирует их как одиночные символы (предположительно, как «s» и «e» соответственно). ,

  • Некоторые символы Юникода определены как игнорируемые, что означает, что они не должны учитываться в порядке сортировки, и сравнение должно перейти к следующему символу. utf8mb4_unicode_ciобращается с этим правильно.

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

Что вы должны использовать?

Почти наверняка нет смысла использовать его utf8mb4_general_ci, поскольку мы оставили точку, когда скорость процессора достаточно низкая, чтобы разница в производительности была важной. Ваша база данных почти наверняка будет ограничена другими узкими местами, кроме этой.

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

Можно привести аргумент, что если скорость важнее для вас, чем точность, вы также можете вообще ничего не делать. Тривиально сделать алгоритм быстрее, если вам не нужно, чтобы он был точным. Таким образом, utf8mb4_general_ciэто компромисс, который, вероятно, не нужен по соображениям скорости и, вероятно, также не подходит по соображениям точности.

Еще одну вещь, которую я добавлю, это то, что даже если вы знаете, что ваше приложение поддерживает только английский язык, ему все равно может потребоваться работать с именами людей, которые часто могут содержать символы, используемые в других языках, для которых столь же важно правильно сортировать , Использование правил Unicode для всего помогает добавить душевного спокойствия, что очень умные люди Unicode очень усердно работали, чтобы заставить сортировку работать должным образом.

Что означают части

Во-первых, ciдля сортировки и сравнения без учета регистра . Это означает, что он подходит для текстовых данных, и случай не важен. Другие типы сопоставления cs(чувствительны к регистру) для текстовых данных, где регистр важен, и binдля того, где кодирование должно соответствовать, бит за битом, что подходит для полей, которые действительно кодируются двоичными данными (включая, например, Base64). Сортировка с учетом регистра приводит к некоторым странным результатам, а сравнение с учетом регистра может привести к тому, что повторяющиеся значения, отличающиеся только регистром букв, поэтому регистры с учетом регистра теряют предпочтение для текстовых данных - если регистр важен для вас, то в противном случае игнорируемая пунктуация и так далее, вероятно, также важно, и двоичное сопоставление может быть более подходящим.

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

И, наконец, utf8mb4конечно же, внутренняя кодировка символов. В этом ответе я говорю только о Unicode-кодировках.

thomasrutter
источник
218
@KahWeeTeng Вы никогда не должны, никогда не используйте utf8_general_ci: это просто не работает. Это возвращение к плохим старым временам ASCII-ступенек с пятидесятилетней давности. Сопоставление без учета регистра в Юникоде невозможно без карты сгиба из UCD. Например, «Σίσυφος» содержит три разных сигмы; или как строчная буква «TSCHüẞ» - «tschüβ», а прописная буква «tschüβ» - «TSCHÜSS». Вы можете быть правы, или вы можете быть быстрым. Поэтому вы должны использовать utf8_unicode_ci, потому что, если вы не заботитесь о правильности, то тривиально сделать это бесконечно быстро.
tchrist
7
Прочитав это, я также обнаружил, что utf8_unicode_ci будет считать любые символы с одинаковым весом сопоставления равными для сравнения на равенство. Это приводит к случаям, когда "か" == "が"или "ǽ" == "æ". Для сортировки это имеет смысл, но может быть удивительно, когда вы выбираете с помощью равенств или имеете дело с уникальными индексами - bugs.mysql.com/bug.php?id=16526
Мэт Шаффер,
4
@DanHorvat Единственная практическая причина ограничиться более старым, более ограниченным подмножеством Unicode в MySQL - это если у вас есть старая версия MySQL, которая не поддерживает более полную utf8mb4. 5.5.3 старше 5 лет. Я понимаю , что Plesk работает по другому расписанию MySQL, но большинство дистрибутивов на MySQL 5.5 сейчас и Plesk 11.x делает поддержку MySQL 5.5 , если ее компоненты.
Томасруттер
22
Я бы не согласился с тем, что использование более нового, более стандартного варианта жалоб является плохой практикой, и я думаю, что подстрекать людей к плохим разработчикам подстрекать к чему-то вроде этого. Вы также можете заметить, что мой ответ в его нынешнем виде гласит: « В новых версиях MySQL используйте utf8mb4, а не utf8», акцент мой.
Томасруттер
24
@DanHorvat utf8mb4- единственный правильный выбор . С utf8вы застряли в каком - то MySQL-только, 3-байтовый вариант UTF8 , что только MySQL (и MariaDB) знают , что делать с. Остальной мир использует UTF8, который может содержать до 4 байтов на символ . Разработчики MySQL неправильно назвали свою кодировку homebrew 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: 9,957 мс
  • с 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%.

nightcoder
источник
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, может отличаться.
Халил Озгюр
2
@ HalilÖzgür - вы ошибаетесь. Я предполагаю, что дело не в значении кодовой точки вне ASCII (которое general_ci будет обрабатывать правильно), а в специфических особенностях, таких как обработка умлаутов, написанных как "Uml ea ute" или некоторых подобных тонкостей.
Томаш Гандор
38

Этот пост описывает это очень хорошо.

Вкратце: utf8_unicode_ci использует алгоритм сопоставления Unicode, как определено в стандартах Unicode, тогда как utf8_general_ci - более простой порядок сортировки, который приводит к «менее точным» результатам сортировки.

Майкл Мэдсен
источник
1
Спасибо. это было мое впечатление. я возьму хит производительности :)
onassar
7
Если вас не заботит правильность, то сделать любой алгоритм бесконечно быстрым. Просто используйте utf8_unicode_ciи сделайте вид, что другого не существует.
tchrist
1
@tchrist, но если вы заботитесь об определенном балансе между правильностью и скоростью, это utf8_general_ciможет быть для вас
Shelvacu
@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, но знайте, что она немного устарела.

Различия с точки зрения производительности очень незначительны.

simhumileco
источник
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.

Полный список совпадений для каждого словосочетания можно найти здесь .

Адам
источник