Índices
A estrutura que faz uma consulta cair de segundos para milissegundos, e o que ela custa na escrita.
Criar um índice é fácil. As decisões estão em qual tipo usar, quais colunas incluir e em que ordem, e quais índices não criar. Os exemplos usam o PostgreSQL; os conceitos valem para os outros bancos relacionais, com nomes e detalhes diferentes.
Tipos de índice
| Tipo | Atende | Uso típico |
|---|---|---|
| Árvore B | Igualdade, intervalo, ordenação, prefixo de texto | O padrão; a resposta certa na maioria dos casos |
| Hash | Só igualdade | Raramente compensa sobre a árvore B |
| GIN | Valores que contêm vários elementos | Colunas JSONB, listas e busca textual |
| GiST | Proximidade e sobreposição | Dados geográficos, intervalos de datas |
| BRIN | Intervalos em tabelas enormes e ordenadas fisicamente | Tabelas de eventos inseridos em ordem de data |
Além do tipo, três variações resolvem problemas comuns.
| Variação | Exemplo | Quando usar |
|---|---|---|
| Parcial | CREATE INDEX ... ON pedidos (criado_em) WHERE status = 'aberto' | Quando as consultas olham sempre para uma fração pequena da tabela |
| Sobre expressão | CREATE INDEX ... ON clientes (lower(email)) | Quando a consulta aplica uma função à coluna |
| Com colunas incluídas | CREATE INDEX ... ON pedidos (cliente_id) INCLUDE (status) | Para responder a consulta só com o índice, sem ler a tabela |
Um índice parcial costuma ser a melhor saída para colunas de baixa seletividade. Um índice em status inteiro ajuda pouco, mas um índice só dos pedidos aberto, que são poucos, é pequeno e rápido.
A ordem das colunas no índice composto
Num índice composto, a ordem das colunas decide quais consultas ele atende. A regra prática:
- Primeiro, as colunas comparadas por igualdade (
=). - Depois, a coluna de intervalo ou de ordenação (
>,<,BETWEEN,ORDER BY).
Para a consulta WHERE cliente_id = $1 AND criado_em > $2 ORDER BY criado_em, o índice certo é (cliente_id, criado_em). Com (criado_em, cliente_id), o banco percorreria todos os pedidos do período e filtraria o cliente depois.
Índice (cliente_id, criado_em) | Atende? |
|---|---|
WHERE cliente_id = 42 | Sim |
WHERE cliente_id = 42 AND criado_em > '2026-01-01' | Sim |
WHERE cliente_id = 42 ORDER BY criado_em DESC LIMIT 20 | Sim, sem ordenar depois |
WHERE criado_em > '2026-01-01' | Não: falta a primeira coluna |
WHERE cliente_id IN (1, 2, 3) | Sim, para cada valor |
Uma consequência útil: o índice (cliente_id, criado_em) torna dispensável um índice só em cliente_id. Índices que são o começo de outro índice costumam ser redundantes.
O que impede o uso do índice
Um índice existe e a consulta continua lenta. As causas mais comuns:
| Consulta | Por que o índice é ignorado | Saída |
|---|---|---|
WHERE lower(email) = ... | O índice guarda email, e não lower(email) | Índice sobre a expressão, ou guardar o e-mail já normalizado |
WHERE email LIKE '%@exemplo.com' | Curinga no início: não há por onde começar na ordem | Índice de trigramas (pg_trgm) ou busca textual |
WHERE date(criado_em) = '2026-03-10' | Função sobre a coluna | Reescrever como intervalo: criado_em >= ... AND criado_em < ... |
WHERE cliente_id = '42' com tipos diferentes | Conversão de tipo na coluna, em alguns bancos | Usar o tipo certo no parâmetro |
WHERE status = 'pago' | Baixa seletividade: o planejador prefere ler tudo | Índice parcial ou aceitar a varredura |
WHERE a = 1 OR b = 2 | Um índice composto não cobre as duas condições | Um índice em cada coluna, ou UNION |
No teste com dois milhões de linhas, a busca por lower(email) levou 251 ms com um índice comum em email, e 0,04 ms depois de criar o índice sobre lower(email).
O que não indexar
| Situação | Motivo |
|---|---|
| Tabelas pequenas, de algumas centenas de linhas | Ler tudo é mais rápido do que consultar o índice |
| Colunas que nenhuma consulta filtra ou ordena | Só custam escrita e espaço |
| Colunas de baixa seletividade, como booleanos | O planejador ignora o índice; use um parcial se preciso |
| Índices que são o começo de outro | Redundantes |
| Tabelas de escrita intensa com poucas leituras | Cada índice atrasa todas as gravações |
Índices fora do relacional
O conceito é o mesmo em outros bancos, com regras próprias:
| Banco | Como funciona |
|---|---|
| MongoDB | Índices de árvore B em campos, compostos e em campos de listas; a regra de ordem das colunas é a mesma |
| DynamoDB | A chave da tabela define o único acesso eficiente; outros acessos exigem índices secundários declarados |
| Elasticsearch | Tudo é indexado por padrão, num índice invertido; o custo aparece no espaço e na escrita |
| Redis | Não há índices secundários: a estrutura de dados escolhida é o índice |
Como decidir o que indexar
- Liste as consultas frequentes, com as colunas do
WHERE, doJOINe doORDER BY. - Crie o menor conjunto de índices que atende todas, compondo colunas quando possível.
- Indexe as chaves estrangeiras usadas em junções ou em exclusões.
- Confira com
EXPLAIN ANALYZEse cada índice é usado, com dados de volume realista. - Revise periodicamente: remova índices sem uso e acompanhe as consultas mais lentas.
O próximo nível mostra como ler um plano de execução, criar índices em produção sem travar a tabela e escrever uma consulta paginada que usa o índice nas cinco linguagens.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Decidir os índices de uma tabela
Quero decidir quais índices uma tabela do meu sistema precisa, sem criar índices demais. Peça que eu descreva a tabela (colunas, volume de linhas e ritmo de gravação) e as consultas que o sistema faz nela, com a frequência de cada uma. Se eu não tiver isso à mão, proponha uma tabela de pedidos de uma loja como exemplo. Depois: 1. Agrupe as consultas pelas colunas que usam no WHERE, no JOIN e no ORDER BY. 2. Proponha o menor conjunto de índices que atende todas, explicando a ordem das colunas de cada um. 3. Aponte onde um índice parcial, sobre expressão ou com colunas incluídas seria melhor. 4. Estime o custo desses índices na escrita e no espaço. 5. Diga quais índices eu não deveria criar, e por quê. Explique cada decisão para que eu consiga repetir o raciocínio em outras tabelas.
Por que a minha consulta ignora o índice
Quero aprender a identificar as causas mais comuns de uma consulta não usar o índice que existe. Explique cada uma, com um exemplo de consulta problemática e a correção: 1. Função aplicada à coluna, como lower(email) ou date(criado_em). 2. LIKE com curinga no início. 3. Conversão de tipo entre a coluna e o parâmetro. 4. Ordem errada das colunas num índice composto. 5. Baixa seletividade. 6. Condições com OR. 7. Estatísticas desatualizadas. Depois me mostre seis consultas com o índice disponível em cada caso, e pergunte se o índice será usado e, se não for, como corrigir. Corrija as minhas respostas.
Tipos especiais de índice
Quero conhecer os tipos de índice além da árvore B e saber quando usar cada um. Pergunte qual banco eu uso. Se for PostgreSQL, explique árvore B, hash, GIN, GiST e BRIN, e as variações parcial, sobre expressão e com colunas incluídas. Se for outro banco, explique os equivalentes disponíveis nele. Para cada tipo, dê um caso de uso concreto, o comando de criação e o que ele custa. Depois me descreva cinco necessidades (busca dentro de um campo JSON, busca de texto com curinga no meio, pedidos abertos numa tabela onde quase todos estão fechados, uma tabela de eventos com bilhões de linhas inseridas em ordem de data, e consultas por proximidade geográfica) e pergunte qual índice eu usaria em cada uma. Corrija as minhas respostas.