DevGuideTK
← Trilha Backend

Í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

TipoAtendeUso típico
Árvore BIgualdade, intervalo, ordenação, prefixo de textoO padrão; a resposta certa na maioria dos casos
HashSó igualdadeRaramente compensa sobre a árvore B
GINValores que contêm vários elementosColunas JSONB, listas e busca textual
GiSTProximidade e sobreposiçãoDados geográficos, intervalos de datas
BRINIntervalos em tabelas enormes e ordenadas fisicamenteTabelas de eventos inseridos em ordem de data

Além do tipo, três variações resolvem problemas comuns.

VariaçãoExemploQuando usar
ParcialCREATE INDEX ... ON pedidos (criado_em) WHERE status = 'aberto'Quando as consultas olham sempre para uma fração pequena da tabela
Sobre expressãoCREATE INDEX ... ON clientes (lower(email))Quando a consulta aplica uma função à coluna
Com colunas incluídasCREATE 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:

  1. Primeiro, as colunas comparadas por igualdade (=).
  2. 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 = 42Sim
WHERE cliente_id = 42 AND criado_em > '2026-01-01'Sim
WHERE cliente_id = 42 ORDER BY criado_em DESC LIMIT 20Sim, 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:

ConsultaPor que o índice é ignoradoSaí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 colunaReescrever como intervalo: criado_em >= ... AND criado_em < ...
WHERE cliente_id = '42' com tipos diferentesConversão de tipo na coluna, em alguns bancosUsar 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 = 2Um índice composto não cobre as duas condiçõesUm í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çãoMotivo
Tabelas pequenas, de algumas centenas de linhasLer tudo é mais rápido do que consultar o índice
Colunas que nenhuma consulta filtra ou ordenaSó custam escrita e espaço
Colunas de baixa seletividade, como booleanosO planejador ignora o índice; use um parcial se preciso
Índices que são o começo de outroRedundantes
Tabelas de escrita intensa com poucas leiturasCada índice atrasa todas as gravações

Índices fora do relacional

O conceito é o mesmo em outros bancos, com regras próprias:

BancoComo funciona
MongoDBÍndices de árvore B em campos, compostos e em campos de listas; a regra de ordem das colunas é a mesma
DynamoDBA chave da tabela define o único acesso eficiente; outros acessos exigem índices secundários declarados
ElasticsearchTudo é indexado por padrão, num índice invertido; o custo aparece no espaço e na escrita
RedisNão há índices secundários: a estrutura de dados escolhida é o índice

Como decidir o que indexar

  1. Liste as consultas frequentes, com as colunas do WHERE, do JOIN e do ORDER BY.
  2. Crie o menor conjunto de índices que atende todas, compondo colunas quando possível.
  3. Indexe as chaves estrangeiras usadas em junções ou em exclusões.
  4. Confira com EXPLAIN ANALYZE se cada índice é usado, com dados de volume realista.
  5. 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.