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

754

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

  • 1
    Смотрите также stackoverflow.com/questions/1036454/…
  • 4
    Если вам нравится utf8[mb4]_unicode_ci , вам может понравиться utf8[mb4]_unicode_520_ci еще больше.
Показать ещё 2 комментария
Теги:
unicode

6 ответов

1186
Лучший ответ

Эти два сопоставления предназначены для кодировки символов UTF-8. Различия в том, как текст сортируется и сравнивается.

Примечание. Начиная с MySQL 5.5.3 вы должны использовать utf8mb4, а не utf8. Оба они относятся к кодировке UTF-8, но у более старого utf8 было ограничение, специфичное для MySQL, не позволяющее использовать символы, пронумерованные выше 0xFFFD.

  • Точность

    utf8mb4_unicode_ci основан на стандарте Unicode для сортировки и сравнения, который точно сортируется в очень широком диапазоне языков.

    utf8mb4_general_ci не реализует все правила сортировки Юникода, что приведет к нежелательной сортировке в некоторых ситуациях, например, при использовании определенных языков или символов.

  • Производительность

    utf8mb4_general_ci работает быстрее при сравнении и сортировке, потому что он принимает кучу ярлыков, связанных с производительностью.

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

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

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

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

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

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

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

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

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

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

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

  • 183
    @KahWeeTeng Вы никогда не должны, когда - либо использовать utf8_general_ci : он просто не работает. Это возвращение к плохим старым временам ASCII-ступенек с пятидесятилетней давности. Сопоставление без учета регистра в Юникоде невозможно выполнить без карты сгиба из UCD. Например, «Σίσυφος» содержит три разные сигмы; или как строчная буква «TSCHüẞ» - «tschüβ», а прописная буква «tschüβ» - «TSCHÜSS». Вы можете быть правы, или вы можете быть быстрым. Поэтому вы должны использовать utf8_unicode_ci , потому что если вас не волнует правильность, сделать тривиально бесконечно быстро.
  • 2
    «Utf8_general_ci очень близок к правильной сортировке Unicode во многих языках, но имеет некоторые неточности в некоторых языках.»: Есть ли влияние на классы символов, я имею в виду на практике, влияет ли это на такие вещи, как LTRIM / RTRIM ?
Показать ещё 19 комментариев
111

Я хотел знать, какова разница в производительности между 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 with ORDER BY):

CREATE 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 мс
benchmark_simple_select() с utf8_unicode_ci: 10271 ms
В этом тесте с использованием utf8_unicode_ci медленнее, чем utf8_general_ci на 3.2%.

benchmark_select_like() с utf8_general_ci: 11441 ms
benchmark_select_like() с utf8_unicode_ci: 12811 ms
В этом тесте с использованием utf8_unicode_ci медленнее, чем utf8_general_ci на 12%.

benchmark_order_by() с utf8_general_ci: 11944 ms
benchmark_order_by() с utf8_unicode_ci: 12887 ms
В этом тесте с использованием utf8_unicode_ci медленнее, чем utf8_general_ci на 7,9%.

  • 13
    Хороший тест, спасибо, что поделились. Я получаю разумно похожие цифры (MySQL v5.6.12 для Windows): 10%, 4%, 8%. Я согласен: прирост производительности utf8_general_ci слишком мал, чтобы его стоило использовать.
  • 9
    1) Но не должен ли этот эталонный тест произвести аналогичные результаты для двух сопоставлений по определению? Я имею в виду CONV(FLOOR(RAND() * 99999999999999), 20, 36) генерирует только ASCII, а не символы Unicode, которые будут обрабатываться алгоритмами сопоставления. 2) Description = 'test' COLLATE ... и Description LIKE 'test%' COLLATE ... обрабатывать только одну строку ("test") во время выполнения, не так ли? 3) В реальных приложениях столбцы, используемые при упорядочении, вероятно, будут проиндексированы, и скорость индексации для разных сопоставлений с реальным текстом, отличным от ASCII, может отличаться.
Показать ещё 1 комментарий
32

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

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

  • 1
    Благодарю. это было мое впечатление. Я возьму удар производительности :)
  • 6
    Если вас не заботит правильность, то сделать любой алгоритм бесконечно быстрым. Просто используйте utf8_unicode_ci и представьте, что другой не существует.
Показать ещё 3 комментария
4

См. руководство по mysql, Unicode Character Sets:

Для любого набора символов Юникода, операции, выполненные с использованием _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 будет быстрее, потому что меньше вычислений.

  • 16
    Нет такой вещи, как «чуть менее правильно». Корректность является булевой характеристикой; он не допускает модификаторов степени. Просто используйте utf8_unicode_ci и притворитесь, что испорченная сломанная версия не существует.
  • 1
    У меня были проблемы с получением 5.6.15 для установки collation_connection, и оказалось, что вы должны передать его в строке SET, например «SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci». Благодарим Матиаса Биненса за решение, вот его очень полезное руководство: mathiasbynens.be/notes/mysql-utf8mb4
Показать ещё 4 комментария
2

Вкратце:

если вам нужен лучший порядок сортировки - используйте utf8_unicode_ci (это предпочтительный метод),

но если вы полностью заинтересованы в производительности - используйте utf8_general_ci, но знайте, что он немного устарел.

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

-4

Есть диаграммы для сортировки символов: http://collation-charts.org/mysql60/mysql604.utf8_general_ci.european.html и http://collation-charts.org/mysql60/mysql604.utf8_unicode_ci.european.html.

Для сохранения значений, таких как "é" и "e" в уникальном столбце, вы должны настроить его сопоставление на "ut8_bin", чтобы избежать дублирования ошибки.

Я не вижу на самом деле преимуществ использования utf8_unicode_ci в повседневном использовании.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню