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

MySQL

В MySQL никогда не используйте «utf8», используйте «utf8mb4».

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

Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘summary’ at row 1

Это клиент UTF-8 и сервер UTF-8 в базе данных UTF-8 с сопоставлением UTF-8. Строка «😃

Но вот загвоздка: MySQL»utf8"это не UTF-8.

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

MySQL developers never fixed this bug. They released a workaround in 2010: новый персонаж набор под названием "utf8mb4".

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

In short:

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

Здесь я сделаю резкое заявление:allПользователи MySQL и MariaDB, которые в настоящее время используют «utf8», должныactuallyиспользуйте «utf8mb4». Никто никогда не должен использовать «utf8».

Какая кодировка Что такое UTF-8?

Joel on Software wrote my favorite introductionЯ перекину мост.

Компьютеры хранят текст в виде единиц и нулей. Первая буква в этом абзаце была сохранена как «01000011», а ваш компьютер нарисовал «C». Ваш компьютер выбрал «C» в два этапа:

  1. Ваш компьютер прочитал «01000011» и определил, что это число 67. Это потому, что 67 былоencodedкак «01000011».
  2. Your computer looked up character number 67 in the Unicode character set, и оказалось, что 67 означает «C».

То же самое произошло и на моем конце, когда я набрал эту букву «C»:

  1. Мой компьютер сопоставил «C» с 67 в наборе символов Unicode.
  2. My computer encoded67, отправив «01000011» на этот веб-сервер.

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

But encoding— это скорее суждение. В Unicode есть слоты для более миллиона символов («C» и «💩» — два таких символа). В самой простой кодировке, UTF-32, каждый символ занимает 32 бита. Это просто, потому что компьютеры имеют целую вечность рассматривали группы по 32 бита как числа, и у них это хорошо получалось, но это бесполезно: пустая трата места.

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

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

Набор символов MySQL «utf8» не согласуется с другими программами.Когда они говорят «💩», это противоречит.

A bit of MySQL history

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

MySQL supported UTF-8 since version 4.1, Это было в 2003 году  — до сегодняшнего стандарта UTF-8,RFC 3629.

The previous UTF-8 standard, RFC 2279, supported up to six bytes per character. MySQL developers coded RFC 2279 in the the first pre-pre-release version of MySQL 4.1 on March 28, 2002.

Затем в сентябре в исходный код MySQL была внесена загадочная однобайтовая поправка: «Теперь UTF8 работает с3 byte sequences only."

Кто это сделал? Почему? Я не могу сказать. Репозиторий кода MySQL, похоже, потерял старые имена авторов, когда он внедрил Git (MySQL использовала BitKeeper, как и ядро ​​Linux). объясняет объясняет изменение.

But I can guess.

Back in 2002, MySQL gave users a speed boostесли бы пользователи могли гарантировать, что каждая строка в таблице имеет одинаковое количество байтов. Для этого пользователи должны объявлять текстовые столбцы как «CHAR». Столбец «CHAR» всегда имеет одинаковое количество символов. Если вы вводите слишком мало символов, он добавляет пробелы в конец; если вы вводите слишком много символов, он обрезает последние.

When MySQL developers first tried UTF-8, with its back-in-the-day six bytes per character, they likely balked: a CHAR(1) column would take six bytes; a CHAR(2) column would take 12 bytes; and so on.

Давайте проясним: это первоначальное поведение, которое так и не было выпущено, былоcorrect. It was well documented and widely adopted, and anybody who understood UTF-8 would agree that it was right.

But clearly, a MySQL developer (or businessperson) was concerned that a user or two would do two things:

  1. Выбирайте столбцы CHAR (в настоящее время формат CHAR является пережитком. В то время MySQL работал быстрее со столбцами CHAR. С 2005 года это не так).
  2. Выберите кодировку этих столбцов CHAR как «utf8».

Я предполагаю, что разработчики MySQL сломали свою кодировку «utf8», чтобы помочь этим пользователям: пользователям, которые 1) пытались оптимизировать пространство и скорость и 2) не смогли оптимизировать скорость и пространство.

Nobody won. Users who wanted speed and space were stillнеправильно использовать столбцы CHAR «utf8», потому что эти столбцы все еще были больше и медленнее, чем должны были быть.И разработчики, которые хотели корректности, были неправы, используя «utf8», потому что эти столбцы все еще были больше и медленнее, чем должны были быть. он не может хранить «💩».

Once MySQL published this invalid character set, it could never fix it: that would force every user to rebuild every database. MySQL finally released UTF-8 support in 2010, с другим именем: «utf8mb4».

Почему это так расстраивает

Очевидно, я был расстроен на этой неделе. Мою ошибку было трудно найти, потому что я был обманут именем «utf8». И я не единственный — почти в каждой статье, которую я нашел в Интернете, «utf8» рекламировался как, ну, UTF-8. .

Название «utf8» всегда было ошибкой, это проприетарный набор символов, он создавал новые проблемы, но не решал проблему, которую должен был решить.

Это ложная реклама.

My take-away lessons

  1. Database systems have subtle bugs and oddities, and you can avoid a lot of bugs by avoiding database systems.
  2. If you needбазу данных, не используйте MySQL или MariaDB.PostgreSQL.
  3. If you needчтобы использовать MySQL или MariaDB, никогда не используйте «utf8».Всегда используйте «utf8mb4», если вы хотите UTF-8.Convert your database now to avoid headaches later.