Помните, никогда не используйте «utf8» в MySQL

база данных MySQL

Недавно я столкнулся с ошибкой, когда пытался сохранить строку UTF-8 в MariaDB, закодированную как «utf8» через Rails, и получил странную ошибку:

__Mon Jun 25 2018 10:35:56 GMT+0800 (CST)____Mon Jun 25 2018 10:35:56 GMT+0800 (CST)__Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘summary’ at row 1__Mon Jun 25 2018 10:35:56 GMT+0800 (CST)____Mon Jun 25 2018 10:35:56 GMT+0800 (CST)__

Я использую клиент с кодировкой UTF-8, сервер также имеет кодировку UTF-8, как и база данных, и даже сохраняемая строка «<... utf-8.>

Суть вещества в том, что «UTF8 MySQL» не на самом деле UTF-8.

«utf8» поддерживает только до трех байтов на символ, в то время как настоящий UTF-8 поддерживает до четырех байтов на символ.

MySQL так и не исправила эту ошибку, они выпустили набор символов под названием «utf8mb4» в 2010 году, который обошёл проблему.

Конечно, они не рекламировали новый набор символов (вероятно, потому что ошибка заставила их чувствовать себя смущенными), так что в Интернете все еще советуют разработчикам использовать «utf8», но все эти предложения неверны.

Краткое резюме выглядит следующим образом:

  • MySQL «utf8mb4» на самом деле является «UTF-8».
  • MySQL "utf8" - это "собственная кодировка", которая может кодировать не так много символов Unicode.

Я должен уточнить здесь: все пользователи MySQL и Mariadb, которые используют «UTF8», должны использовать «UTF8MB4», никогда не используйте «UTF8».

Итак, что закодировано? Что такое UTF-8?

Все мы знаем, что компьютеры используют 0 и 1 для хранения текста. Например, символ «С» хранится как «01000011», тогда компьютеру необходимо пройти два шага при отображении этого символа:

  1. Компьютер читает «01000011» и получает число 67, потому что 67 закодировано как «01000011».
  2. Компьютер искал 67 в наборе символов Unicode и нашел «C».

такой же:

  1. Мой компьютер сопоставляет «C» с 67 в наборе символов Unicode.
  2. Мой компьютер кодирует 67 как «01000011» и отправляет его на веб-сервер.

Почти все веб-приложения используют набор символов Unicode, потому что нет причин использовать какой-либо другой набор символов.

Набор символов Unicode содержит миллионы символов. Простейшей кодировкой является UTF-32, использующая 32 бита на символ. Сделать это проще всего, потому что все это время компьютеры обрабатывали 32 бита как числа, а лучше всего компьютеры работали с числами. Но проблема в том, что это пустая трата места.

UTF-8 может сэкономить место.В UTF-8 для символа «C» требуется всего 8 бит, а для некоторых менее часто используемых символов, таких как «», требуется 32 бита. Другие символы могут использовать 16 или 24 бита. Такая статья, если она закодирована в UTF-8, занимает всего около четверти пространства UTF-32.

Набор символов MySQL "utf8" не совместим с другими программами, и его так называемый "" действительно может быть куском...

Краткая история MySQL

Почему разработчики MySQL делают «utf8» недействительным? Возможно, мы сможем найти ответ в журнале коммитов.

MySQL поддерживает UTF-8, начиная с версии 4.1, которая была выпущена в 2003 году, а используемый сегодня стандарт UTF-8 (RFC 3629) появился позже.

Устаревший стандарт UTF-8 (RFC 2279) поддерживает до 6 байтов на символ. 28 марта 2002 г. разработчики MySQL использовали RFC 2279 в первой предварительной версии MySQL 4.1.

В сентябре того же года в исходный код MySQL внесли корректировку: «UTF8 теперь поддерживает только последовательности до 3 байт».

Кто отправил этот код? Зачем ему это делать? Этот вопрос неизвестен. После перехода на Git (MySQL начинался с BitKeeper) многие имена коммиттеров в кодовой базе MySQL были потеряны. В списке рассылки за сентябрь 2003 г. не было найдено никаких подсказок, объясняющих это изменение.

Но я могу попытаться угадать.

В 2002 году MySQL приняла решение: если пользователи могут гарантировать, что каждая строка таблицы данных использует одинаковое количество байтов, то MySQL может значительно повысить производительность. Для этого пользователю необходимо определить текстовый столбец как «CHAR», каждый столбец «CHAR» всегда имеет одинаковое количество символов. Если количество вставленных символов меньше определенного числа, MySQL добавит пробелы после него.Если вставленные символы превышают определенное количество, лишняя часть будет усечена.

Разработчики MySQL использовали 6 байтов на символ, когда они впервые попробовали UTF-8, CHAR(1) использовал 6 байтов, CHAR(2) использовал 12 байтов и так далее.

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

Однако ясно, что разработчики или поставщики MySQL обеспокоены тем, что пользователи будут делать две вещи:

  1. Используйте CHAR для определения столбца (CHAR уже устарел, но тогда было быстрее использовать CHAR в MySQL, но не с 2005 года).
  2. Установите кодировку столбца CHAR на «utf8».

Я предполагаю, что разработчики MySQL хотели помочь пользователям, которые хотели беспроигрышного варианта в пространстве и скорости, но они напортачили с кодировкой «utf8».

Вот и получается, что победителей нет. Пользователи, которые хотят выиграть в пространстве и скорости, когда они используют столбцы CHAR «utf8», на самом деле используют больше места и медленнее, чем ожидалось. И пользователи, которые хотят корректности, не могут сохранять символы типа "", когда они используют кодировку "utf8".

После того, как этот недопустимый набор символов был выпущен, MySQL не смогла его исправить, и всем пользователям потребовалось перестроить свои базы данных. В конце концов, MySQL перевыпустил «utf8mb4» в 2010 году для поддержки истинной UTF-8.

Почему эта штука сводит людей с ума

Я был зол на неделю из-за этой проблемы. Я был обманут "utf8" и потратил много времени на поиск ошибки. Но я не должен быть единственным, почти все статьи в Интернете рассматривают «utf8» как настоящую UTF-8.

«utf8» можно рассматривать только как проприетарный набор символов, он принес нам новые проблемы, но не решен.

Суммировать

Если вы используете MySQL или MariaDB, не используйте кодировку «utf8», вместо этого используйте «utf8mb4». здесь(Матиас, не груби, не голодай /notes/MySQL…) содержит руководство по преобразованию кодировки символов существующей базы данных из «utf8» в «utf8mb4».

Английский оригинал:medium.com/@Адам Хупер…