Bancos de dados relacionais são o coração de muitas aplicações. Quando as consultas começam a ficar lentas, a experiência do usuário sofre e os custos de infraestrutura aumentam. Este artigo apresenta técnicas práticas para otimizar consultas e estrutura de dados.
1. Entendendo o Plano de Execução
A primeira coisa a fazer é analisar EXPLAIN (PostgreSQL) ou EXPLAIN ANALYZE (MySQL). Ele mostra como o otimizador planeja acessar os dados.
- Seq Scan indica leitura completa da tabela, geralmente sinal de falta de índice.
- Index Scan mostra que um índice está sendo usado.
- Bitmap Index Scan combina múltiplos índices.
- Nested Loop, Hash Join, Merge Join, escolha de algoritmo de join impacta performance.
Checklist de Análise
- O plano usa índices adequados?
- Quantas linhas são estimadas vs. reais?
- Há Sort ou Hash Aggregate custosos?
- O custo total está dentro do esperado?
2. Estratégias de Indexação
Índices B-Tree (padrão)
- Ideais para buscas de igualdade e intervalos.
- Crie índices nas colunas usadas em WHERE, JOIN, ORDER BY.
Índices Parciais
CREATE INDEX idx_orders_status_pending ON orders (status) WHERE status = 'pending';
Reduz o tamanho do índice ao focar apenas em linhas relevantes.
Índices Compostos
Ordene as colunas no índice na mesma ordem que aparecem nas cláusulas WHERE e ORDER BY.
CREATE INDEX idx_sales_date_customer ON sales (sale_date, customer_id);
Índices GIN/GIST (PostgreSQL)
- Úteis para colunas JSONB, arrays, full-text search.
- Exemplo:
CREATE INDEX idx_data_json ON events USING GIN (data);
3. Particionamento de Tabelas
Dividir tabelas grandes em partições menores melhora leitura e manutenção.
- Range Partitioning, por data (ex.:
orders_2025_q1). - List Partitioning, por enumeração (ex.:
status = 'completed'). - Hash Partitioning, distribuição uniforme.
Exemplo de Range Partition (PostgreSQL)
CREATE TABLE orders ( id UUID PRIMARY KEY, order_date DATE NOT NULL, status TEXT NOT NULL, total NUMERIC ) PARTITION BY RANGE (order_date); CREATE TABLE orders_2025_q1 PARTITION OF orders FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
4. Normalização vs. Desnormalização
- Normalização reduz redundância, facilita manutenção.
- Desnormalização pode melhorar leitura ao evitar joins complexos.
- Avalie trade-offs: se a maioria das consultas são read-heavy, considere tabelas de leitura otimizadas.
5. Configurações de Servidor
- shared_buffers (PostgreSQL), 25% da RAM.
- work_mem, memória por operação de sort/join.
- innodb_buffer_pool_size (MySQL), 70-80% da RAM.
- max_connections, ajuste conforme carga.
6. Monitoramento Contínuo
- Use pg_stat_statements (PostgreSQL) ou performance_schema (MySQL) para identificar queries lentas.
- Configure alertas de slow query log.
- Ferramentas como pgBadger, Percona Toolkit ajudam a analisar logs.
7. Checklist de Otimização
- Analisar planos de execução das queries críticas.
- Criar índices adequados (B-Tree, parciais, compostos).
- Avaliar necessidade de particionamento.
- Revisar configuração de memória do servidor.
- Monitorar e registrar queries lentas.
- Revisar modelo de dados (normalização vs. desnormalização).
Conclusão
A otimização de bancos de dados não é um evento único, é um processo iterativo. Comece analisando os gargalos, aplique indexação inteligente, ajuste a configuração do servidor e monitore continuamente. Com essas práticas, você reduz latência, economiza recursos e oferece uma experiência mais fluida aos usuários.
Quais desafios de performance você encontrou em seus bancos de dados? Compartilhe nos comentários!
