Performance testing: pgbench e simulação de carga real
1. Introdução ao Teste de Performance em Bancos de Dados
1.1. Por que testar performance: gargalos, escalabilidade e SLA
Testar a performance de um banco de dados não é um luxo — é uma necessidade para qualquer aplicação que precise crescer sem perder qualidade. Gargalos como consultas lentas, contenção de locks ou I/O de disco insuficiente podem transformar uma aplicação responsiva em uma experiência frustrante. Testes de performance ajudam a identificar esses pontos antes que eles afetem usuários reais, garantindo que o banco atenda aos SLAs (Acordos de Nível de Serviço) estabelecidos.
1.2. Diferença entre teste de carga, teste de estresse e teste de volume
- Teste de carga: mede o comportamento do sistema sob cargas esperadas (número típico de usuários simultâneos).
- Teste de estresse: aumenta a carga além do normal para descobrir o ponto de ruptura.
- Teste de volume: foca na quantidade de dados armazenados, verificando como a performance se degrada com tabelas grandes.
1.3. Ferramentas populares: pgbench, sysbench, HammerDB
- pgbench: nativo do PostgreSQL, simples e eficaz para testes padronizados e customizados.
- sysbench: multi-banco (MySQL, PostgreSQL), flexível para cenários de CPU, memória e I/O.
- HammerDB: interface gráfica, suporta TPC-C e TPC-H, ideal para testes mais elaborados.
Para este artigo, focaremos exclusivamente no pgbench.
2. Instalação e Configuração Básica do pgbench
2.1. Pré-requisitos
O pgbench acompanha a instalação padrão do PostgreSQL. Você precisa de um banco de dados com permissões de superusuário.
# Verificar se o pgbench está disponível
pgbench --version
# Criar um banco de teste (como superusuário)
createdb teste_performance
2.2. Inicialização do ambiente de teste
O comando pgbench -i cria as tabelas padrão: pgbench_accounts, pgbench_branches, pgbench_history e pgbench_tellers.
# Inicializar com escala 100 (10 milhões de linhas em pgbench_accounts)
pgbench -i -s 100 teste_performance
O parâmetro -s define o fator de escala. Cada unidade representa 100.000 linhas em pgbench_accounts. Escolha um valor que se aproxime do volume real da sua aplicação.
2.3. Parâmetros essenciais
-c: número de clientes simultâneos (simula usuários concorrentes)-t: número de transações por cliente-T: duração do teste em segundos (substitui-t)
# Exemplo: 10 clientes, cada um executando 1000 transações
pgbench -c 10 -t 1000 teste_performance
# Exemplo: 20 clientes durante 60 segundos
pgbench -c 20 -T 60 teste_performance
3. Simulação de Carga Real com Scripts Customizados
3.1. Estrutura de um script SQL customizado
O pgbench aceita scripts SQL com variáveis especiais. Cada linha é uma transação, e o \set permite definir variáveis.
3.2. Exemplos de scripts realistas: e-commerce
Imagine um cenário de e-commerce: consultar pedidos, verificar estoque e atualizar status.
-- script_ecommerce.sql
\set client_id random(1, 100000)
\set product_id random(1, 50000)
\set quantity random(1, 5)
BEGIN;
SELECT o.order_id, o.status
FROM orders o
WHERE o.client_id = :client_id
LIMIT 10;
SELECT p.stock_quantity
FROM products p
WHERE p.product_id = :product_id;
UPDATE products
SET stock_quantity = stock_quantity - :quantity
WHERE product_id = :product_id
AND stock_quantity >= :quantity;
END;
3.3. Uso de variáveis aleatórias
O pgbench oferece funções como random() e variáveis de sistema (:scale, :client_id) para simular comportamentos reais.
-- script_vendas.sql
\set aid random(1, 100000 * :scale)
\set bid random(1, 1 * :scale)
\set tid random(1, 10 * :scale)
\set delta random(-5000, 5000)
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;
Execute com:
pgbench -c 10 -T 30 -f script_vendas.sql teste_performance
4. Métricas e Relatórios de Performance
4.1. Saída padrão do pgbench
Após a execução, o pgbench exibe:
transaction type: script_vendas.sql
scaling factor: 100
query mode: simple
number of clients: 10
number of threads: 1
duration: 30 s
number of transactions actually processed: 4521
latency average = 66.384 ms
tps = 150.678 (including connections establishing)
tps = 150.789 (excluding connections establishing)
- TPS: transações por segundo — quanto maior, melhor.
- Latência média: tempo médio por transação.
- Para percentis (p50, p90, p99), use
--loge ferramentas externas.
4.2. Análise de logs detalhados
pgbench -c 10 -T 30 -f script_vendas.sql --log=pgbench_log.csv teste_performance
# O arquivo pgbench_log.csv conterá:
# client_id, transaction_no, time, interval_time
Use --aggregate-interval=5 para médias a cada 5 segundos.
4.3. Monitoramento adicional durante o teste
Conecte-se ao banco em paralelo:
-- No banco de teste
SELECT * FROM pg_stat_activity WHERE state = 'active';
-- Para consultas lentas
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
5. Interpretação de Resultados e Identificação de Gargalos
5.1. Latência alta vs. TPS baixo
- Latência alta + TPS baixo: provável gargalo de CPU ou I/O.
- Latência baixa + TPS baixo: contenção de locks ou configuração errada de pool de conexões.
- Latência crescente com mais clientes: escalabilidade limitada (ponto de saturação).
5.2. Correlação com recursos do sistema
# Monitorar CPU, memória e I/O durante o teste
top -p $(pgrep -f postgres)
iostat -x 1
5.3. Casos comuns
- Lock contention: muitas transações tentando modificar a mesma linha.
- Índices ausentes: consultas com
EXPLAIN ANALYZEmostrando sequential scans. - Checkpoints frequentes:
shared_bufferspequeno oucheckpoint_timeoutbaixo.
6. Estratégias para Otimização com Base nos Testes
6.1. Ajuste de configuração do PostgreSQL
-- Exemplo de parâmetros para workload de escrita intensa
shared_buffers = '2GB' # 25% da RAM
work_mem = '64MB' # para consultas complexas
checkpoint_completion_target = 0.9
checkpoint_timeout = '15min'
6.2. Criação e revisão de índices
EXPLAIN ANALYZE SELECT * FROM orders WHERE client_id = 123;
-- Se mostrar Sequential Scan, criar índice:
CREATE INDEX idx_orders_client_id ON orders(client_id);
Repita o teste e compare o TPS.
6.3. Refinamento do script de carga
- Reduza transações desnecessárias (ex.: consultas que não impactam o fluxo principal).
- Use batch inserts quando possível.
-- Em vez de 100 inserts individuais, use:
INSERT INTO orders (client_id, product_id, quantity)
SELECT random(1,100000), random(1,50000), random(1,5)
FROM generate_series(1,100);
7. Boas Práticas e Cuidados em Ambiente de Produção
7.1. Isolamento do ambiente de teste
Nunca execute testes de carga em produção. Use um banco dedicado ou um snapshot restaurado.
# Criar banco a partir de um dump
createdb teste_producao_snapshot
pg_restore -d teste_producao_snapshot /caminho/do/dump.sql
7.2. Repetibilidade
Para comparar resultados, mantenha a mesma escala, número de clientes e seed aleatória:
pgbench -i -s 100 teste_performance
pgbench -c 10 -T 60 -f script.sql --random-seed=42 teste_performance
7.3. Documentação dos cenários
Registre para cada teste:
- Versão do PostgreSQL
- Parâmetros de configuração
- Script usado
- Métricas obtidas (TPS, latência, percentis)
- Recursos do servidor (CPU, RAM, disco)
Referências
- Documentação oficial do pgbench (PostgreSQL) — Referência completa sobre sintaxe, parâmetros e opções do pgbench.
- PostgreSQL Performance Tuning with pgbench — Guia prático de ajuste de performance usando pgbench como ferramenta de teste.
- Using pgbench for Realistic Load Testing — Artigo da CyberTec sobre como criar scripts customizados para simular cargas reais.
- pg_stat_statements: Identifying Slow Queries — Como usar pg_stat_statements para monitorar consultas durante testes de carga.
- PostgreSQL Checkpoint Tuning Guide — Guia da EnterpriseDB sobre ajuste de checkpoints para melhorar performance em workloads de escrita.