Back

Entendendo a Indexação de Banco de Dados para Melhor Desempenho

Entendendo a Indexação de Banco de Dados para Melhor Desempenho

Suas consultas ao banco de dados estão levando segundos quando deveriam levar milissegundos. O culpado? Índices ausentes ou mal projetados. A indexação de banco de dados é a diferença entre escanear milhões de linhas e pular diretamente para os dados que você precisa—mas fazer isso errado pode desacelerar as escritas e inflar seu armazenamento.

Este artigo aborda os fundamentos da indexação de banco de dados, desde índices B-tree e compostos até técnicas avançadas como índices de cobertura e planos de consulta. Você aprenderá como indexar tipos de dados modernos, incluindo campos JSON e de texto, e, mais importante, como evitar a armadilha de desempenho da sobre-indexação em bancos de dados modernos.

Principais Conclusões

  • Índices de banco de dados trocam leituras mais rápidas por escritas mais lentas e aumento de armazenamento
  • Índices B-tree lidam com a maioria das consultas de forma eficiente, enquanto índices especializados atendem dados JSON e de texto
  • A ordem das colunas em índices compostos determina o suporte às consultas—colunas de igualdade devem vir primeiro
  • Índices de cobertura permitem varreduras apenas de índice ao incluir todas as colunas da consulta
  • Sobre-indexação prejudica o desempenho de escrita—monitore e remova índices não utilizados regularmente

O Que os Índices de Banco de Dados Realmente Fazem

Pense em consultar uma tabela users com 10 milhões de linhas:

SELECT * FROM users WHERE email = 'user@example.com';

Sem um índice, o PostgreSQL ou MySQL deve verificar cada linha individualmente—uma varredura completa da tabela. Com um índice em email, o banco de dados pula diretamente para a linha correspondente usando uma estrutura B-tree, normalmente reduzindo o tempo de busca de O(n) para O(log n).

Um índice é essencialmente uma cópia ordenada de colunas específicas com ponteiros de volta para as linhas completas. O trade-off: leituras mais rápidas ao custo de escritas mais lentas (o índice deve ser atualizado) e armazenamento adicional (tipicamente 10-20% do tamanho da tabela por índice).

Índices B-tree: O Motor de Trabalho dos Bancos de Dados

Índices B-tree alimentam a maioria das consultas de banco de dados. Eles mantêm os dados em uma estrutura de árvore balanceada onde cada nó contém múltiplas chaves ordenadas. Este design minimiza a E/S de disco—crucial já que o acesso ao disco é ordens de magnitude mais lento que o acesso à memória.

CREATE INDEX idx_users_created_at ON users(created_at);

B-trees se destacam em:

  • Verificações de igualdade (WHERE status = 'active')
  • Consultas de intervalo (WHERE created_at > '2024-01-01')
  • Ordenação (ORDER BY created_at)
  • Buscas por prefixo (WHERE email LIKE 'john%')

PostgreSQL e MySQL InnoDB usam variantes B+tree onde todos os dados residem em nós folha, tornando as varreduras de intervalo mais eficientes. MongoDB usa estruturas B-tree similares para seus índices, embora bancos de dados de documentos adicionem complexidade com indexação de campos aninhados.

Índices Compostos: A Ordem das Colunas Importa

Índices compostos cobrem múltiplas colunas, mas a ordem das colunas é crítica:

CREATE INDEX idx_events_user_date ON events(user_id, created_at);

Este índice suporta:

  • WHERE user_id = 123
  • WHERE user_id = 123 AND created_at > '2024-01-01'

Mas NÃO:

  • WHERE created_at > '2024-01-01' (sem user_id)

O índice funciona da esquerda para a direita. Coloque colunas usadas para igualdade primeiro, depois intervalos. Para um endpoint de API que filtra eventos por usuário e intervalo de datas, este índice composto elimina a necessidade de índices separados em cada coluna.

Índices de Cobertura e Planos de Consulta

Um índice de cobertura inclui todas as colunas necessárias por uma consulta, permitindo varreduras apenas de índice:

-- Exemplo PostgreSQL
CREATE INDEX idx_orders_covering 
ON orders(user_id, status, created_at) 
INCLUDE (total_amount);

Para esta consulta:

SELECT total_amount FROM orders 
WHERE user_id = 123 AND status = 'completed';

O banco de dados nunca toca a tabela—tudo vem do índice. Use EXPLAIN para verificar:

EXPLAIN (ANALYZE, BUFFERS) SELECT ...;

Procure por “Index Only Scan” no PostgreSQL ou “Using index” no MySQL. Planos de consulta revelam se seus índices estão realmente sendo usados. Varreduras sequenciais de alto custo indicam índices ausentes, enquanto índices não utilizados desperdiçam recursos.

Indexando Campos JSON e de Texto

Aplicações modernas armazenam dados não estruturados que requerem índices especializados:

Indexação JSON (PostgreSQL)

CREATE INDEX idx_metadata_gin ON products 
USING GIN (metadata);

-- Suporta consultas como:
SELECT * FROM products 
WHERE metadata @> '{"category": "electronics"}';

Índices GIN lidam com consultas de contenção JSON de forma eficiente, embora sejam maiores e mais lentos para atualizar do que B-trees. Para tabelas com alta taxa de escrita, indexar apenas caminhos JSON específicos pode ser mais eficiente do que indexar documentos inteiros.

Busca de Texto Completo

CREATE INDEX idx_posts_text ON posts 
USING GIN (to_tsvector('english', content));

-- Permite:
SELECT * FROM posts 
WHERE to_tsvector('english', content) @@ 'database & indexing';

Índices de texto permitem capacidades de busca semelhantes ao Google, mas podem dobrar os requisitos de armazenamento para tabelas com muito texto.

Evitando Sobre-Indexação em Bancos de Dados Modernos

Mais índices nem sempre são melhores. Cada índice:

  • Desacelera operações INSERT/UPDATE/DELETE (deve atualizar o índice)
  • Aumenta o armazenamento (10-20% por índice)
  • Adiciona sobrecarga de manutenção (fragmentação, reconstruções)

Sinais de sobre-indexação:

  • Tabelas com alta taxa de escrita com 5+ índices
  • Índices duplicados (ex: (a, b) e (a))
  • Índices não utilizados (verifique pg_stat_user_indexes ou sys.dm_db_index_usage_stats)
  • Tamanho do índice excedendo o tamanho da tabela

Melhores práticas:

  1. Comece com índices em chaves primárias e chaves estrangeiras
  2. Adicione índices baseados em logs de consultas lentas, não em suposições
  3. Consolide com índices compostos sempre que possível
  4. Remova índices não utilizados após monitoramento por 30+ dias
  5. Considere índices parciais para tabelas grandes com consultas filtradas:
CREATE INDEX idx_orders_recent 
ON orders(created_at) 
WHERE created_at > '2024-01-01';

Seletividade de Índice e Desempenho no Mundo Real

Seletividade de índice—a razão entre valores únicos e total de linhas—determina a eficácia. Um índice em uma coluna booleana (2 valores) tem baixa seletividade e raramente ajuda. Um índice em email (todos únicos) tem seletividade perfeita.

Monitore métricas de produção reais:

  • Tempo de execução de consulta antes/depois da indexação
  • Taxas de acerto de índice
  • Impacto no desempenho de escrita
  • Crescimento de armazenamento

Ferramentas como pgAdmin, MySQL Workbench ou MongoDB Compass fornecem análise visual de consultas. Para monitoramento de produção, considere DataDog ou New Relic.

Conclusão

Indexação eficaz de banco de dados requer entender seus padrões de consulta, não seguir regras rígidas. Comece com índices B-tree em colunas nas cláusulas WHERE, ORDER BY e JOIN. Use índices compostos estrategicamente, considerando a ordem das colunas. Aproveite índices de cobertura para consultas com alta taxa de leitura e índices especializados para dados JSON e de texto.

Mais importante, meça tudo. Use EXPLAIN para entender planos de consulta, monitore estatísticas de uso de índice e acompanhe o impacto real tanto no desempenho de leitura quanto de escrita. O objetivo não é indexar tudo—é indexar exatamente o que sua aplicação precisa.

Perguntas Frequentes

Não. Indexe colunas baseado na frequência de consultas e seletividade. Colunas com baixa seletividade como booleanos raramente se beneficiam de índices. Foque em colunas usadas em consultas lentas e considere índices compostos para consultas com múltiplas condições.

Use EXPLAIN ANALYZE no PostgreSQL ou EXPLAIN no MySQL para ver planos de execução de consultas. Verifique tabelas do sistema como pg_stat_user_indexes ou sys.dm_db_index_usage_stats para rastrear o uso de índices ao longo do tempo. Índices não utilizados após 30 dias são candidatos para remoção.

Cada índice desacelera operações de escrita, pois o banco de dados deve atualizar todos os índices relevantes. O armazenamento aumenta em 10-20% por índice. A sobrecarga de manutenção cresce com fragmentação e requisitos de reconstrução. Cinco ou mais índices em tabelas com alta taxa de escrita frequentemente indicam sobre-indexação.

Use índices compostos quando consultas filtram em múltiplas colunas juntas. O banco de dados pode usar um índice composto de forma mais eficiente do que combinar múltiplos índices individuais. A ordem importa: coloque condições de igualdade primeiro, depois intervalos. Um índice composto em user_id e created_at atende ambas as colunas juntas.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay