← Voltar ao blog

MySQL: por que utf8 não é UTF-8 (e como usar utf8mb4)

No MySQL, utf8 aceita só 3 bytes e quebra emoji: o erro Incorrect string value é o sintoma. Veja como converter banco, coluna e conexão para utf8mb4.


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

  1. Faça backup lógico e teste o restore.
  2. Inventarie charset e collation por coluna.
  3. Corrija dado já corrompido antes de converter.
  4. Converta o banco, depois as tabelas com CONVERT TO.
  5. Ajuste a conexão da aplicação para utf8mb4.
  6. Revise colunas de índice longo e o Row_format.
  7. Teste gravação de emoji, acento e texto com 4 bytes.
  8. Confira relatório e exportação: arquivo CSV também precisa sair em UTF-8.

Checklist

  • Sei que utf8 no MySQL significa utf8mb3.
  • 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_format e 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: