trilha

System Design/02 - Componentes/Databases2 min

SQL Tuning

Perguntas-guia
  • Que problema isso resolve?
  • Quando usar / quando NÃO usar?
  • Qual o principal trade-off?
  • Como isso falha em produção?

Conceito

Otimizar consultas e schema antes de escalar infraestrutura. É o passo de maior retorno e menor custo — e frequentemente o único necessário.

O primeiro comando

EXPLAIN ANALYZE SELECT ...

O que procurar na saída:

Sinal Significa
Seq Scan em tabela grande Falta índice
Nested Loop com muitas linhas Junção ruim; falta índice na chave
rows estimado ≫ real (ou vice-versa) Estatísticas desatualizadas
Sort em disco work_mem insuficiente
Filtro aplicado depois do scan O índice não está sendo usado

Índices — o que importa

Conceito Efeito
Índice composto A ordem das colunas importa: (a, b) serve para a e a+b, não para b sozinho
Covering index Contém todas as colunas da query — não toca a tabela
Índice parcial WHERE ativo = true — menor e mais rápido
Seletividade Índice em coluna com 2 valores distintos raramente ajuda
Custo de escrita Cada índice desacelera INSERT/UPDATE

O que invalida o índice

Função sobre a coluna (WHERE upper(nome) = ...), LIKE '%x' com curinga à esquerda, tipo incompatível na comparação, e OR entre colunas de índices diferentes.

Trade-offs

Índice é o exemplo mais claro do trade-off leitura/escrita: acelera consulta e desacelera toda escrita, além de ocupar espaço. Tabelas com dezenas de índices "por precaução" têm escrita lenta e índices que ninguém usa — vale auditar quais são efetivamente acionados.

Os problemas mais comuns, em ordem de frequência:

N+1 — uma consulta para a lista e mais uma para cada item. É Chatty IO com roupa de ORM, e a correção é JOIN ou eager loading.

SELECT * — traz colunas que ninguém usa, impede covering index e infla tráfego. Ver Extraneous Fetching.

Paginação por OFFSETOFFSET 100000 faz o banco descartar 100 mil linhas antes de devolver 20. Paginação por cursor (WHERE id > ?) é constante e estável sob escrita concorrente.

Transação longa — segura locks e, no Postgres, impede a limpeza de versões antigas, inflando a tabela.

Exemplo prático

-- 4,2 s: Seq Scan em 8 milhões de linhas
SELECT * FROM pedidos WHERE cliente_id = 42 ORDER BY criado_em DESC LIMIT 20;

CREATE INDEX idx ON pedidos (cliente_id, criado_em DESC);
-- 3 ms

Três milissegundos contra quatro segundos — mais de mil vezes mais rápido, com uma linha e sem custo de infraestrutura.

A ordem das colunas no índice é o que faz funcionar: cliente_id filtra e criado_em DESC já entrega ordenado, eliminando o passo de Sort. Invertida, o índice serviria muito menos.

É esse tipo de ganho que justifica a regra: meça e ajuste antes de adicionar réplica, cache ou shard.

Relacionado


Parte de Databases · roadmap.sh/system-design

Buscar

Busca por título, seção e texto das notas