Read replicas: escalando leituras com segurança
Sistemas de banco de dados relacionais frequentemente enfrentam um gargalo clássico: o nó primário (leader) precisa lidar com todas as operações de escrita e, simultaneamente, atender consultas de leitura. Conforme a aplicação cresce, as consultas de leitura — relatórios pesados, dashboards em tempo real, APIs de busca — consomem CPU, memória e I/O, degradando a performance das escritas. A solução mais adotada para esse cenário é a implantação de read replicas. Este artigo explora os fundamentos, a arquitetura, a configuração e as armadilhas desse padrão de escalabilidade horizontal.
1. Fundamentos de Read Replicas
Uma read replica é uma cópia do banco de dados primário que recebe e aplica continuamente as alterações geradas no líder. O mecanismo central é a replicação de WAL (Write-Ahead Log). No PostgreSQL, por exemplo, o primário envia registros de WAL para as réplicas, que os reaplicam localmente.
Existem três modos de replicação:
- Statement-based: replica comandos SQL completos (
INSERT,UPDATE,DELETE). Simples, mas pode gerar inconsistências se os comandos usarem funções não-determinísticas (comoNOW()ouRANDOM()). - Row-based: replica as linhas modificadas (antes e depois). Mais seguro e preciso, porém gera mais tráfego de rede. É o padrão no MySQL 8.0+ e recomendado no PostgreSQL.
- Mixed: combina os dois modos — usa statement-based para comandos seguros e row-based para comandos não-determinísticos.
Casos de uso típicos incluem:
- Dashboards e BI: consultas agregadas pesadas que não precisam de dados em tempo real.
- APIs de consulta: endpoints que listam produtos, histórico de pedidos ou perfis de usuário.
- Relatórios noturnos: processamento de grandes volumes de dados sem impactar o OLTP primário.
2. Arquitetura e Topologias de Replicação
A topologia mais comum é single leader com múltiplas réplicas:
Primário (escrita)
├── Réplica 1 (leitura)
├── Réplica 2 (leitura)
└── Réplica 3 (leitura)
Todas as escritas vão para o primário; as réplicas atendem apenas leituras. O tráfego de replicação é assíncrono por padrão, o que introduz latência (lag).
Para ambientes muito grandes, usa-se cadeia de replicação (cascading replicas):
Primário
└── Réplica A (intermediária)
└── Réplica B (folha)
Isso reduz a carga de WAL no primário, mas aumenta o lag total.
Em implantações multi-região, réplicas são posicionadas geograficamente próximas aos usuários finais para reduzir latência de leitura. A replicação entre regiões é quase sempre assíncrona.
3. Configuração e Provisionamento de Réplicas
Criar uma réplica no PostgreSQL envolve:
- Fazer um snapshot do primário (via
pg_basebackupou snapshot do filesystem). - Configurar os parâmetros no
postgresql.confda réplica:
wal_level = replica
hot_standby = on
max_connections = 200
max_wal_senders = 5
wal_level: deve serreplicaoulogicalpara permitir replicação.hot_standby: permite consultas na réplica enquanto ela aplica WAL.max_wal_senders: número máximo de conexões de replicação simultâneas.
Para monitorar o lag, use pg_stat_replication:
SELECT application_name, state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
(EXTRACT(EPOCH FROM now() - replay_lag) * 1000)::bigint AS lag_ms
FROM pg_stat_replication;
4. Roteamento de Consultas: Estratégias e Ferramentas
De nada adianta ter réplicas se a aplicação não as utiliza corretamente. Três estratégias principais:
Balanceamento no driver/cliente
Bibliotecas como psycopg2 (Python) ou pgx (Go) permitem configurar múltiplos endpoints. Exemplo com libpq:
host=primario,replica1,replica2 port=5432 dbname=meubd
target_session_attrs=read-write # força escrita no primário
Uso de proxies
Ferramentas como PgBouncer (com modo de réplica), HAProxy e pgbouncer-rr roteiam consultas com base no tipo de comando (SELECT vs INSERT/UPDATE/DELETE). Exemplo de configuração HAProxy:
frontend db_frontend
bind *:5432
use_backend replicas if { ssl_fc_has_crt } # exemplo simplificado
backend replicas
server replica1 10.0.1.10:5432 check
server replica2 10.0.1.11:5432 check
Separação explícita no código
A abordagem mais segura: a aplicação usa duas conexões — uma para escrita (primário) e outra para leitura (pool de réplicas). Exemplo conceitual:
# Pseudocódigo
conexao_escrita = criar_conexao("primario:5432")
conexao_leitura = criar_conexao_pool(["replica1:5432", "replica2:5432"])
def buscar_produtos():
return conexao_leitura.executar("SELECT * FROM produtos")
def criar_pedido(dados):
return conexao_escrita.executar("INSERT INTO pedidos ...")
5. Consistência de Dados e Stale Reads
O maior desafio das read replicas é a consistência eventual. Devido ao lag de replicação, uma consulta na réplica pode retornar dados obsoletos. Exemplo:
-- Usuário insere um pedido no primário (T0)
INSERT INTO pedidos (id, status) VALUES (1001, 'PENDENTE');
-- A réplica ainda não recebeu a mudança (T0 + 100ms)
SELECT * FROM pedidos WHERE id = 1001; -- Retorna vazio! (stale read)
Técnicas de mitigação:
- Session stickiness: fixa um cliente sempre na mesma réplica durante a sessão.
- Leitura imediata no primário: após uma escrita crítica (ex: confirmação de pagamento), força a leitura seguinte no primário.
pg_sleepcontrolado: em casos extremos, espera um tempo fixo antes de ler na réplica (não recomendado como padrão).synchronous_commitesynchronous_standby_names: garante que uma réplica específica confirme a escrita antes de retornar ao cliente. Aumenta latência de escrita, mas elimina stale reads para aquela réplica.
Configuração no primário:
synchronous_commit = on
synchronous_standby_names = 'replica_critica'
6. Failover e Manutenção de Réplicas
Quando o primário falha, uma réplica precisa ser promovida. Ferramentas como Patroni e repmgr automatizam esse processo.
Promoção manual:
pg_ctl promote -D /var/lib/postgresql/replica
Para manutenção sem downtime:
- Drenar conexões: redirecione o tráfego de leitura para outras réplicas.
- Parar a réplica e aplicar atualizações de schema ou patches.
- Reiniciar e permitir que ela recupere o lag.
Atualizações de schema (DDL) em réplicas merecem cuidado. No PostgreSQL, DDLs são replicadas via WAL, então uma migração executada no primário automaticamente chega às réplicas. Contudo, se a réplica estiver com lag, a tabela pode ficar temporariamente inconsistente. Para migrações críticas, use replicação lógica (publicação/assinatura) que permite aplicar DDLs seletivamente.
7. Monitoramento e Alertas para Réplicas
Métricas essenciais:
- Lag em bytes e segundos: via
pg_stat_replicationepg_wal_lsn_diff. - Taxa de WAL recebida:
pg_stat_wal_receiverna réplica. - Conexões ativas: número de sessões de leitura concorrentes.
- Idade das transações mais antigas: evita
transaction ID wraparound.
Ferramentas recomendadas:
- Prometheus + postgres_exporter: coleta métricas de replicação.
- Grafana: dashboards com alertas para lag > 10s ou réplica offline.
- Check_pgactivity (Nagios/Icinga): monitora
streamingereplay_lag.
Exemplo de alerta via SQL:
SELECT CASE
WHEN (pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) > 100000000)
THEN 'ALERTA: Lag superior a 100MB'
ELSE 'OK'
END AS status
FROM pg_stat_replication;
Conclusão
Read replicas são uma ferramenta poderosa para escalar leituras em bancos relacionais, mas exigem planejamento cuidadoso. A escolha da topologia, o roteamento correto de consultas, o monitoramento constante do lag e a estratégia de failover determinam o sucesso da implantação. Ignorar a consistência eventual ou negligenciar o monitoramento pode levar a dados obsoletos e indisponibilidade. Quando bem configuradas, as réplicas permitem que o sistema lide com milhares de consultas simultâneas sem sacrificar a performance das escritas.
Referências
- PostgreSQL Documentation: Chapter 27. High Availability, Load Balancing, and Replication — Documentação oficial do PostgreSQL sobre replicação, failover e balanceamento de carga.
- MySQL Documentation: Replication — Guia completo de replicação no MySQL, incluindo modos statement-based, row-based e mixed.
- Patroni: A Template for High Availability PostgreSQL — Ferramenta open-source para automação de failover e gerenciamento de clusters PostgreSQL.
- HAProxy Documentation: Database Load Balancing — Como configurar HAProxy para rotear tráfego de banco de dados entre primário e réplicas.
- PostgreSQL Replication Monitoring with Prometheus — Guia prático para monitorar lag e métricas de replicação usando postgres_exporter e Prometheus.