No MySQL, utf8 não é UTF-8 completo: é um alias de utf8mb3, limitado a 3 bytes por caractere. Emoji, símbolos raros e boa parte dos caracteres do plano suplementar não entram — a coluna rejeita ou trunca. utf8mb4 aceita 4 bytes e resolve o problema, mas exige mudar banco, tabela, coluna e conexão.
Este guia mostra o sintoma exato do erro, a conversão segura e os dois detalhes que quebram a migração: collation e limite de índice.
O que significa utf8 no MySQL?
utf8 é o nome antigo do utf8mb3: mesma codificação, teto de 3 bytes. Serve para a maior parte do português e do latim, mas não para emoji nem para caracteres acima do BMP.
| Charset | Bytes por caractere | Emoji | Situação |
|---|---|---|---|
latin1 |
1 | não | legado |
utf8 (utf8mb3) |
até 3 | não | depreciado no MySQL 8 |
utf8mb4 |
até 4 | sim | padrão no MySQL 8.0 |
utf8mb4 + utf8mb4_0900_ai_ci |
até 4 | sim | padrão de collation no 8.0 |
No MySQL 8.0, banco novo já nasce em utf8mb4. O problema aparece em banco criado antes, em tabela com charset explícito e em conexão que negocia latin1.
Qual erro aparece quando a coluna não aceita o caractere?
O erro clássico é este, e ele mostra o byte que não coube:
Error Code: 1366. Incorrect string value: '\xF0\x9F\x98\x80' for column
'CLIENTES.OBSERVACAO' at row 1
\xF0\x9F\x98\x80 é o emoji 😀 em UTF-8: 4 bytes. Em utf8mb3 não existe representação válida. Dependendo do modo SQL, o MySQL pode truncar em silêncio em vez de recusar — o que é pior, porque o dado chega corrompido no banco.
-- confirma o charset real da coluna
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'sistema'
AND CHARACTER_SET_NAME IS NOT NULL
ORDER BY TABLE_NAME, COLUMN_NAME;
Como converter banco, tabela e coluna para utf8mb4?
A conversão tem três níveis e a ordem importa: banco, tabela, conexão.
-- 1) banco: define o padrão para objetos novos
ALTER DATABASE sistema
CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- 2) tabela: converte dados existentes, não só o default
ALTER TABLE CLIENTES
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CONVERT TO reescreve os dados na codificação nova. É operação pesada: em tabela grande, faça em janela de manutenção, com backup e em blocos se necessário. ALTER TABLE ... DEFAULT CHARACTER SET só muda o padrão de colunas futuras — não converte o que já está gravado, e é a confusão mais comum.
Antes de converter, verifique se o texto está íntegro. Se o banco já tem dado corrompido por conversão anterior, converter de novo só espalha o problema — nesse caso, o caminho é corrigir o dado antes (ou restaurar do backup anterior à corrupção).
A conexão também precisa ser utf8mb4?
Sim, e é o ponto mais esquecido. Coluna em utf8mb4 com conexão em latin1 corrompe na entrada, mesmo com o banco certo.
<?php
// PDO: charset no DSN, na abertura da conexão
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=sistema;charset=utf8mb4',
$user,
$pass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
<?php
// mysqli: define o charset depois de conectar
$conn = new mysqli('127.0.0.1', $user, $pass, 'sistema');
$conn->set_charset('utf8mb4');
Em cliente de linha de comando, SET NAMES utf8mb4 faz o mesmo. Sem isso, o servidor interpreta os bytes na codificação padrão da conexão e o acento vira dois caracteres errados.
Confira o que está valendo na sessão:
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
Qual collation escolher?
A collation define comparação e ordenação, não o armazenamento. Três opções cobrem quase tudo:
| Collation | Comportamento | Quando usar |
|---|---|---|
utf8mb4_0900_ai_ci |
Unicode 9.0, sem diferenciar acento e caixa | padrão do MySQL 8 |
utf8mb4_unicode_ci |
Unicode 4.0, sem diferenciar acento e caixa | compatibilidade com 5.7 |
utf8mb4_bin |
compara byte a byte | código, hash, dado sensível a caixa |
O detalhe que morde: _ai_ci significa accent-insensitive, case-insensitive. João e Joao são considerados iguais em WHERE NOME = 'Joao' e em índice único. Para nome de pessoa isso costuma ser desejável; para login, apelido único ou código de produto, não. Nesses casos, use utf8mb4_bin na coluna.
E o limite de índice?
utf8mb4 usa até 4 bytes por caractere. Em versões antigas do InnoDB, com formato de linha COMPACT, o prefixo de índice era limitado a 767 bytes — o que dava 191 caracteres. Daí o VARCHAR(191) que aparece em migrações dessa época.
No MySQL 5.7+ com DYNAMIC (padrão atual), o limite sobe para 3072 bytes, ou 768 caracteres. Não é preciso cortar para 191 em instalação nova:
SHOW TABLE STATUS WHERE Name = 'CLIENTES'\G
A coluna Row_format mostra o formato em uso. COMPACT ou REDUNDANT ainda podem exigir prefixo curto em índice — mais um motivo para converter a tabela, não só o charset.
Passo a passo da conversão
- Faça backup lógico e teste o restore.
- Inventarie charset e collation por coluna.
- Corrija dado já corrompido antes de converter.
- Converta o banco, depois as tabelas com
CONVERT TO. - Ajuste a conexão da aplicação para
utf8mb4. - Revise colunas de índice longo e o
Row_format. - Teste gravação de emoji, acento e texto com 4 bytes.
- Confira relatório e exportação: arquivo CSV também precisa sair em UTF-8.
Checklist
- Sei que
utf8no MySQL significautf8mb3. - Todas as colunas de texto estão em
utf8mb4. - A conexão negocia
utf8mb4. - A collation foi escolhida por caso de uso, não por padrão.
- Coluna de unicidade sensível a acento usa
_bin. -
Row_formate limite de índice conferidos. - Backup feito antes do
CONVERT TO. - Teste com emoji passou em gravação e leitura.
Perguntas frequentes
Por que o emoji não grava mesmo com a coluna em utf8?
Porque utf8 no MySQL é utf8mb3, com teto de 3 bytes. Emoji ocupa 4. A coluna precisa ser utf8mb4, e a conexão também.
Preciso recriar as tabelas para mudar de utf8 para utf8mb4?
Não é preciso recriar à mão: ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 reescreve os dados. Em tabela grande, planeje janela de manutenção e backup.
Trocar a collation muda os dados gravados?
Não muda os bytes, mas muda comparação, ordenação e regra de unicidade. utf8mb4_0900_ai_ci trata João e Joao como iguais; utf8mb4_bin trata como diferentes.
Conclusão
O nome utf8 é a armadilha: no MySQL ele não é UTF-8 completo. Trocar para utf8mb4 resolve o armazenamento, mas só funciona com a conexão alinhada, a collation escolhida por caso de uso e o limite de índice conferido. Feito nos três níveis, emoji e acento param de ser loteria.
Se o problema é o texto que já chega errado na aplicação, vale revisar o caminho inteiro — e o Sqlify ajuda quando o ponto é a query: descreva em português e receba o SQL com os filtros e parâmetros corretos, sem concatenação de string.
Leia também: