К основному содержимому

MySQL кодировка utf8mb4: поддержка emoji и Unicode

MySQL / MariaDB · 19.04.2026 · 6 мин чтения
Иллюстрация к статье «MySQL кодировка utf8mb4: настройка для поддержки emoji и Unicode»

Почему utf8 в MySQL не поддерживает emoji

MySQL реализовал кодировку utf8 как 3-байтовую, что покрывает большинство символов, но не emoji (они занимают 4 байта в UTF-8). При попытке сохранить emoji в utf8-колонке MySQL молча обрезает строку до первого проблемного символа или возвращает ошибку, в зависимости от режима sql_mode. utf8mb4 — настоящая 4-байтовая UTF-8, поддерживающая полный Unicode, включая emoji и редкие иероглифы.

Настройка my.cnf для utf8mb4 по умолчанию

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
character-set-filesystem = utf8mb4

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4
sudo systemctl restart mysql

Проверка текущей кодировки

mysql -u root -p -e "
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';"

Изменение кодировки существующей базы данных

-- Изменить charset базы данных
ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Изменить charset таблицы
ALTER TABLE mytable CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Изменить колонку
ALTER TABLE mytable MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Конвертация всех таблиц в базе данных

-- Генерация команд для всех таблиц
SELECT CONCAT(
    'ALTER TABLE `', TABLE_NAME, '` ',
    'CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
) AS migration_sql
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'mydb' AND TABLE_TYPE = 'BASE TABLE';

Разбор типичных ошибок при конвертации

  • Incorrect string value: '\xF0\x9F\x98\x80' for column — колонка всё ещё в utf8 или latin1, а приложение уже пытается писать emoji; конвертация из раздела выше должна пройти на этой таблице до повторной вставки.
  • Specified key was too long; max key length is 767 bytes — индекс на VARCHAR(255) при utf8mb4 занимает до 1020 байт (255×4), что превышает лимит старого формата таблиц; решение — сократить длину индексируемого поля, перейти на движок InnoDB с большими префиксами или включить innodb_large_prefix.
  • Illegal mix of collations for operation 'UNION' — при выборочной конвертации часть таблиц осталась на utf8_general_ci, а часть уже на utf8mb4_unicode_ci; запросы, объединяющие такие таблицы, падают, пока collation не приведены к одному значению.
  • Data too long for column после конвертации — 4-байтовые символы занимают больше места, чем закладывалось при расчёте длины VARCHAR; иногда после перехода нужно увеличить длину колонки, а не только сменить charset.

Collation: unicode_ci vs general_ci vs bin

CollationОписаниеКогда использовать
utf8mb4_unicode_ciТочное Unicode-сравнение (CI = case insensitive)Большинство сайтов (рекомендуется)
utf8mb4_general_ciУпрощённое сравнение, чуть быстрееВысоконагруженные проекты
utf8mb4_binПобайтовое сравнение (CS = case sensitive)Пароли, хэши, чувствительные к регистру данные
utf8mb4_0900_ai_ciMySQL 8.0 стандарт Unicode 9.0MySQL 8.0+

Настройка в PHP (PDO)

<?php
$pdo = new PDO(
    'mysql:host=localhost;dbname=mydb;charset=utf8mb4',
    'user', 'password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'
    ]
);

При обновлении с utf8 на utf8mb4 проверьте, что индексы VARCHAR-колонок не превышают 767 байт. При utf8mb4 один символ может занять до 4 байт, поэтому VARCHAR(191) — практический максимум для индексируемых строк при стандартном InnoDB (innodb_large_prefix=OFF).

Сравнение utf8 и utf8mb4

Критерийutf8 (устаревший)utf8mb4
Байт на символДо 3До 4
Поддержка emojiНетДа
Макс. длина VARCHAR с индексом (старый InnoDB)255191
Занимаемое место на дискеМеньшеНемного больше
Рекомендация для новых проектовНе использоватьИспользовать всегда

Когда конвертация не так проста

  • В базе уже есть данные с эмодзи, сохранённые как «обрезанные» строки в utf8, — восстановить исходный текст из повреждённых байт невозможно, только заново получить данные из источника.
  • Таблица использует полнотекстовый индекс (FULLTEXT) — при смене charset его нужно пересоздать заново, конвертация «на лету» не переносит статистику индекса автоматически.
  • Приложение подключается к MySQL через несколько разных драйверов (PHP, Java, cron-скрипты на Python) — charset нужно синхронно поменять во всех местах подключения, иначе часть сервисов продолжит писать битые данные даже после конвертации таблиц.

Как проверить, что конвертация прошла успешно

# Убедиться, что все таблицы базы в utf8mb4
mysql -u root -p -e "
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA='mydb' AND TABLE_COLLATION NOT LIKE 'utf8mb4%';"
# Пустой результат означает, что конвертированы все таблицы

# Тестовая вставка emoji
mysql -u root -p mydb -e "
CREATE TEMPORARY TABLE emoji_test (t TEXT) CHARACTER SET utf8mb4;
INSERT INTO emoji_test VALUES ('Тест emoji: ');
SELECT * FROM emoji_test;"

Если запрос к information_schema не находит таблиц вне utf8mb4, а тестовая вставка проходит без ошибки Incorrect string value — конвертация завершена корректно на уровне базы данных.

Частые вопросы

Рискует ли конвертация базы в utf8mb4 потерей данных в существующих строках, или это всегда безопасная операция?

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

Работает ли utf8mb4 на практике медленнее utf8 из-за того, что символ может занимать больше байт?

Разница в большинстве обычных нагрузок почти неощутима: дополнительный байт на символ увеличивает объём данных на диске и в памяти незначительно, а само сравнение строк по выбранному collation работает так же. Заметнее влияние проявляется только там, где индексы на длинных строковых полях упираются в ограничение длины ключа и требуют укорачивания или смены настроек движка.

Если в приложении уже есть испорченные из-за старой кодировки данные с эмодзи, можно ли восстановить исходный текст?

Нет, если символ был обрезан на уровне кодировки до первого проблемного байта, часть исходной информации уничтожена безвозвратно и никакая смена charset её не вернёт. Единственный надёжный путь — заново получить такие данные из первоисточника, будь то исходный экспорт, лог приложения или внешний сервис, откуда они когда-то попали в базу.

Нужно ли менять кодировку и в коде приложения, или достаточно поменять её только в базе данных?

Менять настройки нужно согласованно в обоих местах: если база уже на utf8mb4, а соединение из приложения по-прежнему открывается со старой кодировкой, сервер субд будет молча преобразовывать символы под старый набор прямо на лету, и те же проблемы с обрезанием эмодзи вернутся. Параметр подключения и настройки базы должны указывать на один и тот же charset одновременно.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке