Tecnologias/02 - Bancos de Dados/01 - Relacionais/02 - MySQL6 min
MySQL
Conceito
A tese do MySQL é simplicidade operacional. Ele venceu a web dos anos 2000 sendo o banco que qualquer pessoa conseguia instalar, replicar e manter — e essa herança ainda define o produto: menos recursos, menos superfície, menos surpresa na operação.
Duas heranças históricas explicam quase tudo o que confunde quem chega do Postgres:
- Motores de armazenamento plugáveis. MyISAM, InnoDB, MEMORY, ARCHIVE… O InnoDB venceu e é o padrão desde o 5.5, mas o MySQL ainda é uma camada SQL sobre um motor, não um sistema único. Por isso o otimizador é historicamente mais simples que o do Postgres.
- Tolerância a dado inválido. Por muitos anos, inserir
'2024-02-31'gerava um aviso, não um erro. OSTRICT_TRANS_TABLESvirou padrão no 5.7, mas a cultura ("o banco aceita, o app valida") ficou.
A decisão que muda seu modelo: índice clusterizado
Este é o fato do InnoDB, e o que mais diferencia MySQL dos outros dois:
No InnoDB, a tabela é a árvore B+ da chave primária. Não existe heap separado. As linhas estão fisicamente ordenadas pela PK, dentro das folhas do índice.
Consequências diretas, todas práticas:
- Busca por PK é imbatível: a folha do índice já contém a linha. Zero acesso extra.
- Índice secundário guarda a PK, não um ponteiro físico. Buscar por um índice secundário e precisar de outras colunas custa dois percursos de árvore (o secundário, depois o clusterizado). No Postgres seria um acesso ao heap.
- PK grande engorda todos os índices. Uma PK
VARCHAR(64)é copiada em cada entrada de cada índice secundário. - PK aleatória fragmenta a escrita. Inserir com UUID v4 significa inserir no meio da árvore, sempre: page splits, cache sujo, I/O aleatório. Com
AUTO_INCREMENTou UUID v7 (ordenável por tempo) as inserções vão sempre no fim — a diferença de throughput em tabela grande é de várias vezes.
Se você lembrar de uma única coisa sobre MySQL, lembre disso. Ver 5. Índices.
Arquitetura em uma tela
- Thread por conexão (com thread pool na versão Enterprise). Conexão é bem mais barata que no Postgres — centenas ou milhares são normais.
- MVCC por undo log: as versões antigas vão para o undo (segmentos de rollback), fora da tabela. Não há bloat estilo Postgres; existe history list length. Ver 3. MVCC e Isolamento na Prática.
- Redo log + doublewrite buffer para durabilidade;
innodb_flush_log_at_trx_commité o botão de durabilidade vs throughput (1= ACID de verdade,2= perde até 1s num crash do SO). - Binlog é o log lógico de replicação — separado do redo. Replicação assíncrona por padrão, com GTID; semi-síncrona é opcional; Group Replication / InnoDB Cluster dá failover automático.
- Isolamento padrão:
REPEATABLE READ— diferente dos outros dois, e a fonte de surpresa nº 1 em migração (3. MVCC e Isolamento na Prática).
Versões: o modelo LTS + Innovation
Desde o 8.1 a Oracle separou as trilhas, o que confunde bastante:
| Trilha | Ritmo | Para quem |
|---|---|---|
| LTS | ~a cada 2 anos, correções por anos | produção que não quer upgrade trimestral |
| Innovation | trimestral, features novas | quem testa e sobe rápido |
Panorama em set/2026:
- 8.4 LTS — a LTS anterior, ainda mantida (8.4.11)
- 9.7 LTS — primeira LTS desde a 8.4 (abr/2026); trouxe JavaScript no banco e JSON duality
- 26.7 Innovation (jul/2026) — a trilha de features; note o salto de numeração para o ano
- 9.0 foi a Innovation que introduziu o tipo
VECTORe DDL atômico de database
Trade-offs
Menos garantias no schema. Não há tipos ricos (nada de array, range, jsonb
indexável no mesmo nível), não há índice parcial, não há RETURNING no INSERT, e
check constraints só passaram a ser realmente aplicadas no 8.0.16. Muita validação
que o Postgres faz por você fica no app.
DDL não é transacional. Este é o trade-off mais caro do dia a dia:
-- No Postgres isto é atômico. No MySQL, NÃO.
START TRANSACTION;
ALTER TABLE pedidos ADD COLUMN cupom varchar(20); -- commit implícito!
ALTER TABLE pedidos ADD COLUMN erro_aqui INT; -- se falhar...
ROLLBACK; -- ...não desfaz a primeira
Uma migração que falha no meio deixa o schema num estado intermediário, e a correção é manual. O 8.0 trouxe atomic DDL (cada instrução isolada é atômica, não fica meio aplicada) — mas não DDL dentro de transação. É por isso que ferramentas como gh-ost e pt-online-schema-change existem no mundo MySQL e não têm equivalente óbvio no Postgres.
Otimizador mais simples. Melhorou muito no 8.0 (CTE, window functions, hash join, histogramas), mas ainda perde do Postgres em consultas complexas — subqueries correlacionadas e agregações em várias tabelas são onde a diferença aparece. Ver SQL Tuning e 3. Subqueries.
O trade-off central: MySQL é mais fácil de operar e menos capaz de garantir. Ele empurra responsabilidade do banco para a aplicação — o que é ótimo quando a aplicação é uma só, e virou o motivo de tantos dados inconsistentes quando são seis.
Exemplo prático
O impacto de escolher a chave primária — a decisão mais barata de tomar certo e mais caro de corrigir:
-- ❌ Antipadrão comum: UUID v4 como PK no InnoDB
CREATE TABLE eventos_ruim (
id CHAR(36) PRIMARY KEY, -- 36 bytes, ordem aleatória
user_id BIGINT NOT NULL,
criado_em DATETIME(6) NOT NULL,
INDEX (user_id) -- este índice carrega os 36 bytes da PK
);
-- ✅ Duas alternativas boas
CREATE TABLE eventos_bom (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, -- 8 bytes, sempre no fim
uuid BINARY(16) NOT NULL UNIQUE, -- id externo, se precisar
user_id BIGINT NOT NULL,
criado_em DATETIME(6) NOT NULL,
INDEX (user_id, criado_em)
);
-- ou: UUID v7 em BINARY(16) — ordenável por tempo, insere sempre no fim
O que muda de fato:
UUID v4 CHAR(36) |
AUTO_INCREMENT / UUID v7 |
|
|---|---|---|
| Local da inserção | meio aleatório da árvore | sempre no fim |
| Page splits | constantes | quase nenhum |
| Tamanho dos índices secundários | +36 B por entrada | +8/16 B por entrada |
| Cache útil | dilui (páginas quentes espalhadas) | concentrado |
Se você precisa de identificador opaco na API (e é uma boa prática), a resposta não é
"PK aleatória": é PK sequencial interna + UNIQUE no identificador externo, ou UUID v7 —
disponível nativamente também no Postgres 18.
Como isso falha em produção
| Sintoma | Causa real |
|---|---|
| Escrita degradando com o tempo | PK aleatória fragmentando o índice clusterizado |
ALTER TABLE travando a tabela por horas |
DDL sem ALGORITHM=INPLACE; use gh-ost/pt-osc |
| Réplica com lag crescente | apply do binlog com pouco paralelismo, ou transação enorme no primário |
| Dado lido logo após escrever não existe | leitura na réplica com replicação assíncrona (read-after-write) |
Acentos e emoji virando ? |
charset utf8 (3 bytes, incompleto) em vez de utf8mb4 |
GROUP BY retornando coluna aleatória |
ONLY_FULL_GROUP_BY desligado — o MySQL "aceita" o que é ambíguo |
Deadlock em UPDATE de faixa |
gap locks do REPEATABLE READ (3. MVCC e Isolamento na Prática) |
| Escrita "perdida" após failover | replicação assíncrona: o primário confirmou e caiu antes de replicar |
utf8 não é UTF-8No MySQL, utf8 é um apelido histórico para utf8mb3 — três bytes por caractere,
o que não cobre emoji nem parte do CJK. O correto é sempre utf8mb4
(com utf8mb4_0900_ai_ci como collation). É a armadilha mais famosa do produto.
Aprofundamento aplicado
Contrato transacional inclui o mecanismo de armazenamento
Uma aplicação precisa conferir engine, isolamento e modo SQL efetivos. O comportamento esperado do InnoDB não deve ser atribuído a qualquer tabela só porque ela está em MySQL.
SELECT @@transaction_isolation, @@autocommit, @@sql_mode;
SHOW CREATE TABLE pedidos;
O exemplo pressupõe uma tabela pedidos existente. Além do schema, examine se as
operações que compõem a regra realmente usam a mesma conexão e transação. Com autocommit,
duas instruções independentes não se tornam uma unidade apenas por serem consecutivas.
Cenário: criar o pedido e reservar estoque precisam confirmar juntos. Uma falha entre as instruções pode deixar pedido sem reserva se cada uma já confirmou sozinha. O tratamento de erro deve abortar a unidade de trabalho e representar retries deliberadamente.
Exercício: provoque erro na segunda escrita em uma transação de estudo. Resultado esperado: após rollback, nenhuma das alterações da unidade permanece. Repita com autocommit para observar a diferença.
Referência: autocommit, commit e rollback.
Relacionado
- 1. PostgreSQL
- 1. Oracle Database
- 1. PostgreSQL vs MySQL vs Oracle
- 3. MVCC e Isolamento na Prática
- 5. Índices
- Replication
- Aurora
Parte de MOC MySQL · dev.mysql.com/doc · Releases de jul/2026