В 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» в два этапа:
- Ваш компьютер прочитал «01000011» и определил, что это число 67. Это потому, что 67 былоencodedкак «01000011».
- Your computer looked up character number 67 in the Unicode character set, и оказалось, что 67 означает «C».
То же самое произошло и на моем конце, когда я набрал эту букву «C»:
- Мой компьютер сопоставил «C» с 67 в наборе символов Unicode.
- 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:
- Выбирайте столбцы CHAR (в настоящее время формат CHAR является пережитком. В то время MySQL работал быстрее со столбцами CHAR. С 2005 года это не так).
- Выберите кодировку этих столбцов 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
- Database systems have subtle bugs and oddities, and you can avoid a lot of bugs by avoiding database systems.
- If you needбазу данных, не используйте MySQL или MariaDB.PostgreSQL.
- If you needчтобы использовать MySQL или MariaDB, никогда не используйте «utf8».Всегда используйте «utf8mb4», если вы хотите UTF-8.Convert your database now to avoid headaches later.