Історично utf8 у MySQL - це utf8mb3: до 3 байтів на символ. Справжній UTF-8 використовує до 4 байтів, і 4-байтові символи (емодзі, частина ієрогліфів, математичні символи) в utf8mb3 не вміщуються.
Що відбувається на практиці: користувач пише коментар з емодзі, і вставка падає з Incorrect string value: '\xF0\x9F\x98\x80', а в нестрогому режимі - текст обрізається на першому емодзі без помилки.
utf8mb4 - повний UTF-8. У MySQL 8 це кодування за замовчуванням (з collation utf8mb4_0900_ai_ci), а utf8mb3 застаріло.
Переведення наявних таблиць:
ALTER DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CONVERT TO перекодовує дані всіх текстових колонок - це перебудова таблиці: на великих таблицях довго, з блокуванням запису. Для продакшену - через gh-ost чи pt-online-schema-change.
Підводні камені переходу:
- Ліміт довжини ключа індексу. У старих версіях і форматах рядка (
COMPACT) ключ обмежений 767 байтами:VARCHAR(255)вutf8mb4- це 1020 байтів, і індекс не створюється. ЗвідсиSchema::defaultStringLength(191)у старих Laravel-проєктах. З форматомDYNAMIC(за замовчуванням у MySQL 8) ліміт - 3072 байти, і проблеми немає. - Зміна collation змінює порівняння: нечутливість до регістру й діакритики (
ai_ci) може зробити «різні» значення однаковими - і унікальний індекс не створиться, поки дублікати не прибрати. - Розмір: тексти, що містять 4-байтові символи, займуть більше; для кирилиці розмір не зміниться (2 байти на символ).
- З'єднання теж має бути
utf8mb4:charsetу конфігурації підключення ('charset' => 'utf8mb4'у Laravel) - інакше дані перекодуються вutf8mb3ще по дорозі.
Правило для нових проєктів: лише utf8mb4 скрізь - сервер, база, таблиці, з'єднання.