utf8 vs utf8mb4 no MySQL: O Guia Técnico Completo
O conjunto de caracteres utf8 do MySQL é um nome enganoso — não é uma implementação UTF-8 verdadeira. Codifica caracteres usando apenas 1 a 3 bytes, o que significa que descarta silenciosamente ou rejeita qualquer ponto de código Unicode acima de U+FFFF, incluindo todos os emoji e uma parte significativa dos caracteres CJK suplementares. utf8mb4 é a implementação UTF-8 correta e completa do MySQL, suportando 1 a 4 bytes por caractere e o intervalo Unicode completo. Para qualquer base de dados de produção criada após 2010, utf8mb4 é a única escolha defensável.
Este guia explica exatamente por que essa distinção é importante, onde o design original do utf8 falhou, como migrar com segurança e como configurar o MySQL corretamente ao nível do servidor, base de dados, tabela e ligação.
O Problema Central: Por Que o utf8 do MySQL É Defeituoso por Design
O padrão de codificação UTF-8 (RFC 3629) define um esquema de largura variável que usa 1 a 4 bytes para representar cada ponto de código Unicode válido — mais de 1,1 milhão de caracteres possíveis. Quando o MySQL introduziu o seu conjunto de caracteres `utf8` na versão 4.1, a implementação foi intencionalmente limitada a 3 bytes por caractere. Isto foi um atalho de engenharia deliberado, não um descuido.
Na época, o formato de linha InnoDB impunha um limite de 767 bytes nos prefixos de chave de índice. Suportar caracteres de 4 bytes teria reduzido o comprimento máximo do prefixo indexado para colunas `VARCHAR`, criando problemas de compatibilidade de índices. O limite de 3 bytes foi uma solução pragmática que se tornou uma responsabilidade a longo prazo.
A consequência prática: qualquer ponto de código Unicode no Plano Multilingue Suplementar (SMP) — pontos de código U+10000 e acima — não pode ser armazenado numa coluna `utf8`. Isto inclui:
- Todos os emoji padrão (U+1F600 e além)
- Símbolos alfanuméricos matemáticos (U+1D400–U+1D7FF)
- Símbolos de notação musical
- Escritas históricas como Linear B, Gótico e Cuneiforme
- Ideogramas CJK Unificados Suplementares (U+20000–U+2A6DF)
- Certos símbolos de moeda e operadores técnicos adicionados em versões recentes do Unicode
Quando uma aplicação tenta inserir um caractere de 4 bytes numa coluna `utf8`, o MySQL retorna um erro `Incorrect string value` ou, se `sql_mode` for permissivo, trunca silenciosamente os dados. O truncamento silencioso é indiscutivelmente o resultado mais perigoso — a sua aplicação não recebe nenhum erro, mas os seus dados estão corrompidos.
utf8mb4: A Implementação Correta
O MySQL introduziu o utf8mb4 na versão 5.5.3 (lançada em 2010) especificamente para resolver esta deficiência. O sufixo `mb4` significa “multi-byte, máximo de 4 bytes”. É um superconjunto estrito de `utf8` — cada caractere representável em `utf8` é identicamente representável em `utf8mb4`. Não há perda de dados ao migrar de `utf8` para `utf8mb4`.
utf8mb4 mapeia diretamente para o padrão UTF-8 RFC 3629. Trata o espaço de código Unicode completo de U+0000 a U+10FFFF sem restrições.
utf8 vs utf8mb4: Comparação de Funcionalidades
| Funcionalidade | utf8 (MySQL) | utf8mb4 |
|---|
| — | — | — |
|---|
| Bytes por caractere | 1–3 | 1–4 |
|---|
| Cobertura Unicode | Apenas BMP (U+0000–U+FFFF) | Completo (U+0000–U+10FFFF) |
|---|
| Suporte a emoji | Não | Sim |
|---|
| CJK suplementar | Não | Sim |
|---|
| Compatível com RFC 3629 | Não | Sim |
|---|
| Prefixo máximo de índice (InnoDB, páginas de 4KB) | 767 bytes | 767 bytes (191 caracteres) |
|---|
| Prefixo máximo de índice (innodb_large_prefix) | 3072 bytes | 3072 bytes (768 caracteres) |
|---|
| Sobrecarga de armazenamento vs latin1 | Idêntico para ASCII | Idêntico para ASCII |
|---|
| Recomendado para novos projetos | Não | Sim |
|---|
| Versão MySQL em que foi introduzido | 4.1 | 5.5.3 |
|---|
Escolhas de Collation no utf8mb4
Selecionar utf8mb4 como conjunto de caracteres é apenas metade da decisão. A collation determina como as strings são comparadas, ordenadas e indexadas. A collation errada causa comportamentos de consulta subtis e difíceis de depurar.
utf8mb4_unicode_ci
Baseada no Algoritmo de Collation Unicode (UCA). Trata corretamente as regras de ordenação específicas de cada idioma. Ligeiramente mais lenta que `utf8mb4_general_ci` devido à lógica de comparação mais complexa, mas a diferença de desempenho é negligenciável em hardware moderno.
utf8mb4_general_ci
Uma collation simplificada que não implementa totalmente o UCA. Mais rápida em benchmarks do início dos anos 2010, mas a vantagem de velocidade é irrelevante nas CPUs atuais. Trata alguns casos extremos incorretamente — por exemplo, considera certos caracteres alemães como equivalentes quando não deveriam ser. Evitar em novos projetos.
utf8mb4_0900_ai_ci
Disponível no MySQL 8.0+. Baseada no Unicode 9.0 com comparação insensível a acentos (`ai`) e insensível a maiúsculas/minúsculas (`ci`). Este é o padrão recomendado para MySQL 8.0 e posterior. É mais rápida que `utf8mb4_unicode_ci` e mais precisa.
utf8mb4_bin
Comparação binária — sensível a maiúsculas/minúsculas, sensível a acentos, sem regras específicas de localidade. Use quando precisar de correspondência exata ao nível de bytes, como para hashes de palavras-passe ou identificadores sensíveis a maiúsculas/minúsculas.
Recomendação: Use `utf8mb4_0900_ai_ci` no MySQL 8.0+. Use `utf8mb4_unicode_ci` no MySQL 5.7 e anteriores.
Implicações de Armazenamento e Índice
Uma preocupação comum ao migrar de utf8 para utf8mb4 é a sobrecarga de armazenamento. Na prática, o impacto é mínimo:
- Caracteres ASCII (U+0000–U+007F) ainda ocupam exatamente 1 byte em ambas as codificações.
- A maioria dos caracteres latinos, gregos, cirílicos, árabes e hebraicos ocupa 2 bytes em ambas as codificações.
- Caracteres CJK no BMP ocupam 3 bytes em ambas as codificações.
- Apenas caracteres suplementares (emoji, CJK suplementar) requerem 4 bytes — e estes simplesmente não eram representáveis em utf8 anteriormente.
A verdadeira preocupação com índices é o limite de prefixo de índice InnoDB de 767 bytes em configurações mais antigas. Com utf8mb4, um pior caso de 4 bytes por caractere significa que um prefixo de índice `VARCHAR` de 191 caracteres atinge o limite de 767 bytes. Com `utf8`, o mesmo limite permitia 255 caracteres. Se tiver colunas `VARCHAR(255)` com índices de coluna completa, poderá encontrar erros `Specified key was too long` durante a migração.
Soluções:
- Ativar `innodb_large_prefix = ON` (MySQL 5.6/5.7) para aumentar o limite para 3072 bytes.
- Usar `ROW_FORMAT=DYNAMIC` ou `ROW_FORMAT=COMPRESSED` nas tabelas afetadas.
- No MySQL 8.0, `innodb_large_prefix` está ativado por padrão e o parâmetro foi removido.
- Encurtar prefixos de índice: `INDEX (column(191))` em vez de `INDEX (column(255))`.
Este é o ponto de falha mais comum na migração e o mais frequentemente subdocumentado nos guias básicos.
Como Migrar uma Base de Dados MySQL de utf8 para utf8mb4
A migração é simples, mas requer precisão. Ignorar qualquer camada — servidor, base de dados, tabela ou ligação — faz com que a sua aplicação reverta silenciosamente para a codificação antiga.
Passo 1: Fazer Backup da Base de Dados
Nunca modifique a codificação de caracteres numa base de dados em produção sem um backup verificado.
“`bash
mysqldump -u username -p –single-transaction –routines –triggers
database_name > database_backup_$(date +%F).sql
“`
O sinalizador `–single-transaction` garante um snapshot consistente para tabelas InnoDB sem bloqueio. Armazene o backup num local separado do servidor de base de dados antes de prosseguir.
Passo 2: Atualizar a Configuração do Servidor MySQL
Edite `/etc/mysql/my.cnf` ou `/etc/mysql/mysql.conf.d/mysqld.cnf` dependendo da sua distribuição:
“`ini
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
For MySQL 5.6/5.7 only — remove on MySQL 8.0
innodb_large_prefix = ON
innodb_file_format = Barracuda
innodb_file_per_table = ON
“`
Reinicie o MySQL:
“`bash
sudo systemctl restart mysql
“`
Passo 3: Converter a Base de Dados
“`sql
ALTER DATABASE database_name
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
“`
Passo 4: Converter Todas as Tabelas
Gere e execute instruções `ALTER TABLE` para cada tabela. Executá-las manualmente em esquemas grandes é propenso a erros. Use esta consulta para gerar as instruções automaticamente:
“`sql
SELECT CONCAT(
'ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
)
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'database_name'
AND TABLE_TYPE = 'BASE TABLE';
“`
Execute cada instrução gerada. A sintaxe `CONVERT TO CHARACTER SET` altera tanto o padrão da tabela como todas as colunas de caracteres existentes numa única operação.
Passo 5: Corrigir Erros de Comprimento de Índice
Se encontrar `Specified key was too long; max key length is 767 bytes`, identifique o índice problemático:
“`sql
— Change full-column index to prefix index
ALTER TABLE table_name DROP INDEX index_name;
ALTER TABLE table_name ADD INDEX index_name (column_name(191));
“`
Para bases de dados WordPress especificamente, a coluna `option_name` da tabela `wp_options` e a coluna `meta_key` de `wp_postmeta` são fontes comuns deste erro.
Passo 6: Verificar a Conversão
“`sql
— Check server-level variables
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
— Check a specific table
SHOW CREATE TABLE table_nameG
— Check all columns in a database
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'database_name'
AND DATA_TYPE IN ('char', 'varchar', 'text', 'tinytext', 'mediumtext', 'longtext');
“`
Cada valor `CHARACTER_SET_NAME` deve mostrar `utf8mb4`.
Passo 7: Atualizar as Strings de Ligação da Aplicação
A codificação do servidor e do esquema não significa nada se a sua aplicação se ligar usando o conjunto de caracteres errado. A codificação ao nível da ligação substitui o padrão do servidor.
PHP (PDO):
“`php
$dsn = 'mysql:host=localhost;dbname=database_name;charset=utf8mb4';
$pdo = new PDO($dsn, $user, $pass, [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
]);
“`
PHP (MySQLi):
“`php
$mysqli = new mysqli('localhost', $user, $pass, $db);
$mysqli->set_charset('utf8mb4');
“`
Python (mysql-connector-python):
“`python
cnx = mysql.connector.connect(
host='localhost', user=user, password=pass,
database=db, charset='utf8mb4', collation='utf8mb4_unicode_ci'
)
“`
Node.js (mysql2):
“`javascript
const pool = mysql2.createPool({
host: 'localhost', user: user, password: pass,
database: db, charset: 'utf8mb4'
});
“`
Não definir o charset da ligação é a razão mais comum pela qual os emoji continuam a falhar na inserção após uma migração supostamente completa.
Considerações Específicas para WordPress
O WordPress usa utf8mb4 como conjunto de caracteres padrão desde a versão 4.2 (abril de 2015). Se estiver a executar uma instalação WordPress numa base de dados mais antiga que nunca foi migrada, o ficheiro `wp-config.php` pode ainda conter:
“`php
define('DB_CHARSET', 'utf8');
“`
Altere para:
“`php
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');
“`
O WordPress também inclui uma rotina de atualização integrada (`maybe_convert_table_to_utf8mb4()`) que é executada durante as atualizações do núcleo. No entanto, esta rotina nem sempre abrange todas as tabelas, particularmente as criadas por plugins. Executar a abordagem manual `ALTER TABLE` descrita acima é mais fiável.
Num ambiente de Alojamento VPS com acesso root, pode automatizar todo este processo com um script shell e agendá-lo como um cron job único, dando-lhe controlo total sobre o timing e o registo.
Considerações de Desempenho
O impacto no desempenho do utf8mb4 versus utf8 é negligenciável para a grande maioria das cargas de trabalho:
- Consultas de leitura: Nenhuma diferença mensurável para caracteres BMP. Os caracteres suplementares requerem um byte adicional de I/O, que é absorvido pelo cache do buffer pool.
- Consultas de escrita: Idênticas para conteúdo ASCII e BMP. Marginalmente superiores para caracteres suplementares.
- Operações de índice: O comprimento máximo de prefixo reduzido (191 vs 255 caracteres para índices de largura total) pode afetar os planos de consulta se tiver índices de coluna completa em colunas `VARCHAR` longas. Audite os seus índices antes e após a migração.
- Memória: O MySQL aloca buffers de largura fixa para operações de string com base no máximo de bytes por caractere. Mudar de utf8 (máximo de 3 bytes) para utf8mb4 (máximo de 4 bytes) aumenta a memória alocada para buffers de ordenação em memória e tabelas temporárias em aproximadamente 33% para operações com muitas strings. Num Servidor Dedicado com RAM suficiente, isto é irrelevante. Num ambiente partilhado com restrições de memória, monitorize `sort_buffer_size` e `tmp_table_size` após a migração.
Quando utf8 Ainda É Aceitável
Existe um conjunto restrito de razões legítimas para manter `utf8`:
- Compatibilidade estrita com sistemas legados: Uma aplicação que usa um ORM ou driver de base de dados sem manutenção que não consegue lidar com caracteres de 4 bytes. Este é um problema de dívida técnica, não uma razão para manter utf8 indefinidamente.
- Bases de dados de arquivo somente leitura: Se uma base de dados nunca receberá novas escritas e os dados existentes não contêm caracteres suplementares, a migração acrescenta risco sem benefício.
- Restrições severas de armazenamento: Em casos extremos — sistemas embebidos ou ambientes com capacidade severamente limitada — a diferença marginal de armazenamento pode ser relevante. Isto não se aplica a nenhum cenário padrão de alojamento web.
Em todos os outros casos, utf8mb4 é a escolha correta. O argumento de que utf8 poupa espaço de armazenamento é tecnicamente verdadeiro apenas para caracteres suplementares, que eram irrepresentáveis em utf8 de qualquer forma. Não está a poupar espaço em dados que não conseguia armazenar.
Escolher o Ambiente de Alojamento Certo para MySQL utf8mb4
A configuração adequada do utf8mb4 requer acesso ao ficheiro de configuração do servidor MySQL (`my.cnf`). Isto exclui a maioria dos ambientes de alojamento partilhado onde não é possível modificar variáveis ao nível do servidor.
Para controlo total sobre a codificação de caracteres MySQL, collation, definições InnoDB e parâmetros de ligação, precisa de um plano de Alojamento VPS com acesso root ou de um Servidor Dedicado. Ambos dão-lhe acesso direto a `/etc/mysql/my.cnf`, a capacidade de reiniciar o serviço MySQL e a liberdade de configurar `innodb_large_prefix`, `ROW_FORMAT` e outros parâmetros que afetam o sucesso da migração para utf8mb4.
Se gerir múltiplas bases de dados ou sites de clientes, um VPS com cPanel fornece uma interface gráfica para gestão de bases de dados, mantendo o acesso ao servidor subjacente necessário para a configuração do conjunto de caracteres. Para equipas que preferem flexibilidade de linha de comandos com um painel leve, os Painéis de Controlo VPS oferecem várias alternativas adequadas a diferentes fluxos de trabalho operacionais.
Para projetos que também requerem transmissão segura de dados, combinar a migração da sua base de dados com um Certificado SSL devidamente configurado garante que os dados codificados em utf8mb4 estão protegidos em trânsito, não apenas em repouso.
Lista de Verificação de Decisões Técnicas
Use esta lista de verificação antes e após qualquer migração de utf8 para utf8mb4:
Pré-migração:
- [ ] Backup `mysqldump` completo verificado e restaurável
- [ ] Versão MySQL confirmada (5.5.3+ necessária para utf8mb4)
- [ ] Estado de `innodb_large_prefix` verificado (ativar se no MySQL 5.6/5.7)
- [ ] Todas as colunas `VARCHAR(255)` com índices de coluna completa identificadas
- [ ] Código de charset de ligação da aplicação revisto e atualizado
- [ ] Janela de manutenção agendada para bases de dados em produção
Pós-migração:
- [ ] `SHOW VARIABLES LIKE 'character_set%'` mostra `utf8mb4` ao nível do servidor
- [ ] `SHOW CREATE TABLE` confirma `utf8mb4` em todas as tabelas convertidas
- [ ] Consulta `information_schema.COLUMNS` confirma que não restam colunas `utf8`
- [ ] `SET NAMES utf8mb4` ao nível da aplicação ou equivalente confirmado no código de ligação
- [ ] Teste de inserção de emoji aprovado numa tabela representativa
- [ ] Linha de base de desempenho de consultas comparada com métricas pré-migração
- [ ] Comprimentos de índice verificados — sem truncamento silencioso de valores indexados longos
FAQ
A migração de utf8 para utf8mb4 causa perda de dados?
Não. utf8mb4 é um superconjunto estrito do utf8 do MySQL. Cada caractere armazenado numa coluna utf8 é identicamente representável em utf8mb4. A migração é não destrutiva para os dados existentes. O único risco são erros de comprimento de índice em colunas `VARCHAR(255)` com índices de coluna completa, que devem ser resolvidos encurtando o prefixo do índice.
Por que os emoji continuam a falhar na inserção após converter as minhas tabelas para utf8mb4?
A causa mais comum é o charset de ligação da aplicação. Se o seu código PHP, Python ou Node.js se ligar sem especificar explicitamente `utf8mb4`, o MySQL usa o padrão `character_set_client` do servidor para essa sessão. Adicione `SET NAMES utf8mb4` ou o parâmetro charset equivalente à sua configuração de ligação.
Qual é a diferença entre utf8mb4_unicode_ci e utf8mb4_0900_ai_ci?
`utf8mb4_unicode_ci` é baseada nas regras de collation do Unicode 4.0 e é a escolha padrão para MySQL 5.7. `utf8mb4_0900_ai_ci` é baseada no Unicode 9.0, é o padrão no MySQL 8.0, e é simultaneamente mais rápida e mais precisa linguisticamente. Use `utf8mb4_0900_ai_ci` no MySQL 8.0+ para novos projetos.
Mudar para utf8mb4 aumentará significativamente o tamanho de armazenamento da minha base de dados?
Na prática, não. Os caracteres ASCII e a maioria dos caracteres BMP usam o mesmo número de bytes em ambas as codificações. Apenas os caracteres suplementares (emoji, CJK suplementar) usam 4 bytes — e esses eram irrepresentáveis em utf8 anteriormente. A sobrecarga de memória para buffers de ordenação aumenta aproximadamente 33% para operações com muitas strings, mas isto é negligenciável em qualquer servidor moderno.
Posso configurar utf8mb4 em alojamento partilhado?
Parcialmente. Pode definir o conjunto de caracteres ao nível da base de dados e da tabela usando instruções SQL `ALTER`, e pode especificar o charset na string de ligação da sua aplicação. No entanto, não pode modificar `my.cnf` nem reiniciar o MySQL em alojamento partilhado. Os padrões ao nível do servidor permanecerão inalterados, o que significa que novas bases de dados criadas através do painel de alojamento podem usar utf8 por padrão. A configuração completa do utf8mb4 requer um VPS ou servidor dedicado com acesso root.
