Performance de Banco de Dados e o Papel dos Índices nas Consultas

Capítulo 1

Tempo estimado de leitura: 8 minutos

+ Exercício

Objetivos comuns de performance em banco de dados

Quando alguém diz “a consulta está lenta”, vale traduzir isso em objetivos mensuráveis. Em bancos de dados, três objetivos aparecem com frequência:

  • Tempo de resposta (latência): quanto tempo uma consulta específica leva para retornar o resultado. Ex.: “listar os pedidos do cliente em até 200 ms”.
  • Throughput (vazão): quantas operações o sistema consegue completar por unidade de tempo. Ex.: “processar 2.000 consultas por segundo”.
  • Previsibilidade: estabilidade do tempo de resposta. Ex.: “95% das consultas abaixo de 300 ms”, evitando picos (cauda longa) que afetam a experiência.

Esses objetivos se conectam diretamente a como o banco lê dados do armazenamento: quanto mais trabalho de leitura e varredura, maior a latência, menor a vazão e pior a previsibilidade.

Como consultas acessam dados: páginas/blocos e o custo de I/O

Internamente, o banco não “lê uma linha” do disco/SSD; ele lê páginas (também chamadas de blocos), que são pedaços fixos de dados (por exemplo, 8 KB, 16 KB etc., dependendo do sistema). Uma página pode conter várias linhas. Isso cria uma consequência importante: mesmo que você precise de poucas linhas, pode acabar lendo muitas páginas até encontrá-las.

Pense em um arquivo físico com fichas: você não pega uma ficha isolada; você puxa uma gaveta inteira (a página) e procura dentro. Se a ficha desejada estiver espalhada por várias gavetas, você abre várias gavetas (I/O).

Leitura sequencial (varredura) vs. acesso por busca

Existem dois padrões comuns de acesso:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Leitura sequencial / varredura (scan): o banco percorre muitas páginas em sequência para avaliar um filtro. É eficiente quando precisa de grande parte da tabela, mas caro quando você quer poucos registros.
  • Acesso por busca (seek/lookup): o banco usa uma estrutura para ir mais direto às páginas relevantes, reduzindo o número de páginas lidas.

Em geral, “lentidão” em consultas de filtro/ordenação costuma estar ligada a muito I/O (muitas páginas lidas) e/ou muita varredura (scan amplo), especialmente quando o resultado final é pequeno.

Por que índices existem: reduzir dados lidos e encurtar o caminho

Um índice é uma estrutura que organiza valores de uma ou mais colunas para permitir que o banco encontre registros com menos leitura. A ideia central é:

  • Reduzir o volume de dados lidos: em vez de ler a tabela inteira, o banco lê uma parte menor (o índice) e só visita as páginas de dados necessárias.
  • Encurtar o caminho até os registros: em vez de “procurar página por página”, o banco segue uma rota mais direta até as chaves/linhas que atendem ao filtro.

Analogia controlada: imagine uma lista telefônica (índice) que aponta rapidamente para a página onde o nome aparece. Sem a lista organizada, você folhearia página por página (scan). Com a lista, você pula para perto do alvo (busca), abrindo menos “gavetas”.

O que o índice “compra” e o que ele “cobra”

Índices costumam melhorar leituras seletivas, mas têm custos:

  • Espaço: ocupam armazenamento e cache.
  • Escrita mais cara: INSERT/UPDATE/DELETE precisam manter o índice atualizado.
  • Planejamento: muitos índices podem confundir o otimizador e aumentar o tempo de manutenção.

Conectando sintoma a causa: exemplos simples (filtros e ordenações)

Vamos usar uma tabela hipotética pedidos:

pedidos(id, cliente_id, status, criado_em, total)

Exemplo 1 — Filtro seletivo: poucos registros retornados

Consulta:

SELECT id, total, criado_em FROM pedidos WHERE cliente_id = 42;

Sintoma: demora mesmo retornando poucas linhas.

Causa provável: sem índice em cliente_id, o banco pode fazer varredura na tabela para checar cada linha, lendo muitas páginas até achar as do cliente 42.

Como o índice ajuda: um índice em cliente_id permite localizar rapidamente as entradas do cliente e visitar apenas as páginas relevantes.

Exemplo 2 — Filtro pouco seletivo: muitos registros retornados

Consulta:

SELECT * FROM pedidos WHERE status = 'PENDENTE';

Sintoma: consulta pesada e com grande volume de dados.

Causa provável: se uma grande parte da tabela está com status = 'PENDENTE', mesmo com índice o banco pode decidir que é melhor varrer (scan) porque acabaria lendo muitas páginas de qualquer forma.

Interpretação prática: índice não é “mágico”; ele tende a ajudar quando o filtro reduz bastante o conjunto de linhas (alta seletividade).

Exemplo 3 — Ordenação: quando o ORDER BY vira gargalo

Consulta:

SELECT id, total FROM pedidos WHERE cliente_id = 42 ORDER BY criado_em DESC;

Sintoma: a consulta filtra rápido, mas fica lenta ao ordenar.

Causa provável: o banco encontra as linhas do cliente, mas precisa ordenar o resultado (operações de sort) porque não consegue ler já na ordem desejada.

Como o índice ajuda: um índice composto que combine filtro e ordenação pode permitir que o banco leia os registros já na ordem, reduzindo ou eliminando o sort.

Exemplo de ideia (conceitual): índice em (cliente_id, criado_em) pode ajudar a filtrar por cliente_id e entregar em ordem de criado_em.

Exemplo 4 — Paginação: “pegar a próxima página” pode custar caro

Consulta:

SELECT id, criado_em FROM pedidos ORDER BY criado_em DESC OFFSET 10000 LIMIT 20;

Sintoma: páginas iniciais rápidas, páginas mais profundas muito lentas.

Causa provável: o banco precisa percorrer/descartar muitas linhas até chegar no offset, lendo muitas páginas e fazendo trabalho que não vira resultado.

Como o índice pode ajudar: um índice em criado_em pode reduzir custo de ordenação, mas o OFFSET alto ainda força “andar” muito. (Aqui, o problema é menos “falta de índice” e mais “forma de paginação”, mas o índice costuma ser parte do diagnóstico.)

Passo a passo prático: diagnosticando lentidão ligada a varredura e I/O

Passo 1 — Identifique o padrão da consulta

  • Tem WHERE com igualdade (=) ou faixa (>=, BETWEEN)?
  • Tem ORDER BY?
  • Retorna poucas linhas ou muitas?
  • Usa paginação (LIMIT/OFFSET)?

Passo 2 — Pergunte: “o banco está varrendo demais?”

Sem entrar em ferramentas específicas, o raciocínio é:

  • Se retorna poucas linhas, mas demora muito, suspeite de scan amplo (muitas páginas lidas para achar poucas linhas).
  • Se ordena e demora, suspeite de sort por falta de caminho já ordenado.
  • Se há picos de tempo, suspeite de cache misses (precisou buscar mais páginas no armazenamento) e de variações no plano.

Passo 3 — Relacione o filtro/ordenação com colunas candidatas a índice

  • Filtros frequentes em colunas com boa seletividade tendem a ser bons candidatos.
  • Combinações comuns de filtro + ordenação podem pedir índice composto.
  • Se a consulta seleciona poucas colunas, um índice que “cubra” a consulta pode reduzir visitas à tabela (dependendo do banco).

Passo 4 — Avalie o custo de escrita e manutenção

  • Tabela muito atualizada (muitos INSERT/UPDATE/DELETE)? Índices demais podem piorar throughput de escrita.
  • Coluna com valores que mudam muito? Atualizações podem gerar mais trabalho de manutenção.
  • Índices redundantes (muito parecidos) podem desperdiçar espaço e cache.

Mapa mental de decisões: quando índices ajudam e quando atrapalham

SituaçãoÍndice tende a ajudar quando…Índice pode atrapalhar quando…
Filtro (WHERE)O filtro reduz bastante as linhas (alta seletividade); você busca um subconjunto pequeno com frequência.O filtro retorna grande parte da tabela; o banco ainda precisará ler muitas páginas (scan pode ser melhor).
Ordenação (ORDER BY)A ordenação é frequente e pode ser atendida pela ordem do índice, reduzindo sort.A ordenação muda muito (muitas colunas/ordens diferentes), levando a muitos índices ou a índices pouco usados.
Consultas com LIMIT pequenoVocê quer “os primeiros N” em uma ordem específica; índice pode permitir parar cedo (early stop).Com OFFSET alto, mesmo com índice pode haver muito trabalho para “pular” registros.
Leituras vs escritasO sistema é mais leitura (read-heavy) e precisa de baixa latência em consultas críticas.O sistema é mais escrita (write-heavy); cada índice extra aumenta custo de INSERT/UPDATE/DELETE.
Colunas usadas em JOINChaves de junção são usadas frequentemente e restringem bem o conjunto de linhas.Junções em colunas pouco seletivas ou com baixa utilidade prática; índice vira custo sem ganho.
Espaço e cacheO índice cabe bem em memória/cache e evita leituras de páginas de dados.Muitos índices competem por cache, piorando previsibilidade e aumentando I/O em picos.

Agora responda o exercício sobre o conteúdo:

Uma consulta com WHERE retorna poucas linhas, mas está lenta. Qual é a explicação mais provável e como um índice pode ajudar?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Quando poucas linhas são retornadas mas a consulta demora, é comum o banco estar lendo muitas páginas (scan e I/O) para encontrar os registros. Um índice na coluna filtrada permite localizar os dados com menos páginas lidas, reduzindo latência e melhorando previsibilidade.

Próximo capitúlo

Como Índices Funcionam na Prática: Estruturas, Chaves e Ponteiros

Arrow Right Icon
Capa do Ebook gratuito Índices e Performance de Banco de Dados para Iniciantes: Como Acelerar Consultas sem Mistério
7%

Índices e Performance de Banco de Dados para Iniciantes: Como Acelerar Consultas sem Mistério

Novo curso

14 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.