Replicação de Banco de Dados
Manter cópias sincronizadas dos dados em múltiplos nós para aumentar disponibilidade, tolerância a falhas e capacidade de leitura — com trade-offs claros entre durabilidade, latência de escrita e consistência de leitura.
Intenção
Replicação mantém cópias sincronizadas dos dados em múltiplos nós para que, se um nó falhar, os dados não sejam perdidos e o sistema continue operando — e, como benefício adicional, para distribuir a carga de leitura entre as cópias.
Um banco de dados em nó único é um ponto único de falha: se o servidor cair, o sistema inteiro fica indisponível. Se o disco falhar sem backup recente, os dados são perdidos. Replicação resolve os dois problemas mantendo cópias dos dados em outros servidores — as replicas — que podem assumir o serviço se o nó principal falhar, e que podem servir leituras enquanto o principal processa escritas.
Problema
Sistemas em produção têm dois requisitos fundamentais que um único nó de banco não consegue garantir ao mesmo tempo:
- Alta disponibilidade: o banco deve estar acessível mesmo durante falhas de hardware, manutenção ou atualizações. Com um único nó, qualquer falha implica downtime.
- Durabilidade dos dados: dados confirmados para o cliente não devem ser perdidos em caso de falha. Um único disco, por mais confiável que seja, tem probabilidade não-nula de falhar.
- Capacidade de leitura insuficiente: em workloads read-heavy (típicos de aplicações web), um único nó pode não ter throughput suficiente para servir todas as leituras com a latência esperada.
Como funciona
Primary/Replica (Master/Slave)
O modelo mais comum de replicação: um nó é designado primary (ou master) e recebe todas as escritas. As alterações são propagadas às replicas via replication log (WAL no PostgreSQL, binlog no MySQL). As replicas servem leituras.
Aplicação
│ │
│ Escritas │ Leituras
▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ Primary │─────►│ Replica 1│ │ Replica 2│
└─────────┘ └──────────┘ └──────────┘
│ replication log
└──────────────────────────────────────►
Aplicação [Leituras]
Toda escrita: Primary
Toda leitura: Replica 1 ou Replica 2 (ou Primary se necessário)
A separação de leituras e escritas em diferentes nós é uma das formas mais simples de aumentar o throughput de leitura sem sharding. Um primary com duas replicas triplica a capacidade de leitura do sistema.
Replicação síncrona vs assíncrona
A diferença entre os dois modos define o trade-off entre durabilidade e latência:
REPLICAÇÃO SÍNCRONA
1. Cliente envia escrita ao Primary
2. Primary replica para ao menos uma Replica
3. Replica confirma recebimento
4. Primary confirma a escrita ao cliente
┌────────┐ ──write──► ┌─────────┐ ──replicate──► ┌─────────┐
│ Client │ │ Primary │ │ Replica │
└────────┘ ◄──ack──── └─────────┘ ◄──────ack───── └─────────┘
RPO = 0 (zero perda de dados confirmados)
Custo: latência de escrita inclui a ida-e-volta até a replica
REPLICAÇÃO ASSÍNCRONA
1. Cliente envia escrita ao Primary
2. Primary confirma a escrita ao cliente imediatamente
3. Primary replica para as Replicas em background
┌────────┐ ──write──► ┌─────────┐
│ Client │ │ Primary │ ──replicate (async)──► ┌─────────┐
└────────┘ ◄──ack──── └─────────┘ │ Replica │
└─────────┘
RPO > 0 (dados replicados com atraso podem ser perdidos se Primary cair)
Benefício: latência de escrita mínima (não espera a replica)
A maioria dos sistemas usa replicação assíncrona como padrão (MySQL, PostgreSQL por padrão) por questões de latência. Replicação síncrona — ou semi-síncrona, que aguarda confirmação de ao menos uma replica — é usada onde durabilidade é crítica, como em sistemas financeiros.
Replication lag
Em replicação assíncrona, há sempre um atraso entre a escrita confirmada no primary e o dado estar disponível nas replicas. Esse atraso é o replication lag.
Em condições normais, o lag é de milissegundos. Mas sob carga alta, durante operações de manutenção ou com replicas em zonas de disponibilidade distantes, o lag pode crescer para segundos ou minutos — e a aplicação não sabe, a menos que monitore explicitamente.
Failover
Quando o primary cai, uma replica precisa ser promovida a primary. O failover pode ser:
- Manual: um operador decide qual replica promover e atualiza a configuração. Mais seguro, mas mais lento — downtime de minutos.
- Automático: um processo de orquestração (ex.: Patroni para PostgreSQL, MHA para MySQL, ou o mecanismo nativo do RDS/Cloud SQL) detecta a falha e promove a replica mais atualizada. Downtime de segundos, mas risco de split-brain se a detecção de falha for um falso positivo.
Multi-primary (Multi-master)
Múltiplos nós aceitam escritas simultaneamente. Aumenta a disponibilidade para escritas — se um primary cair, os outros continuam operando — mas introduz o problema de conflitos de escrita: dois nós recebem escritas diferentes para o mesmo registro ao mesmo tempo.
A resolução de conflitos é inerentemente complexa: timestamp-based (a escrita mais recente vence, com risco de perder a outra), application-defined (a aplicação define a lógica de merge) ou last-write-wins (simples, mas potencialmente destrutivo). Multi-primary deve ser evitado salvo necessidade real — como replicação geográfica onde cada região precisa aceitar escritas localmente.
Quando usar
- Alta disponibilidade e failover: qualquer sistema de produção que não pode tolerar downtime prolongado durante falhas de hardware deve usar replicação. É o requisito mínimo para um SLA de disponibilidade significativo.
- Workloads read-heavy: quando a proporção de leituras para escritas é alta (típico em aplicações web), distribuir as leituras entre replicas aumenta o throughput sem a complexidade do sharding.
- Backup online sem impactar o primary: rodar backups pesados diretamente na replica evita que o processo de backup consuma recursos do primary e afete a latência de produção.
- Relatórios e analytics: queries analíticas longas podem ser direcionadas a uma replica dedicada, isolando o workload OLAP do OLTP no primary.
Quando evitar
- Multi-primary, salvo necessidade específica: a resolução de conflitos de escrita simultânea é custosa de implementar corretamente. Sistemas que parecem precisar de multi-primary frequentemente podem ser redesenhados com sharding ou com Primary/Replica e aceitar consistência eventual.
- Como substituto para sharding: replicação aumenta a capacidade de leitura, mas não a capacidade de escrita — todas as escritas ainda passam pelo primary. Se o gargalo é escrita, replicação não resolve.
Prós e contras
Prós
- Alta disponibilidade: falha do primary não implica perda de dados nem downtime prolongado — uma replica assume.
- Escalabilidade de leitura: throughput de leitura cresce linearmente com o número de replicas.
- Durabilidade: dados existem em múltiplos nós e geografias, reduzindo drasticamente o risco de perda permanente.
- Isolamento de workload: replicas dedicadas para analytics ou backups protegem o primary das queries mais pesadas.
- Latência geográfica: replicas em regiões próximas aos usuários reduzem a latência de leitura.
Contras
- Consistência eventual: leituras de replicas podem retornar dados desatualizados se o replication lag for alto.
- Gargalo de escrita persiste: todas as escritas passam pelo primary — replicação não resolve sobrecarga de escrita.
- Complexidade operacional: monitorar lag de replicação, gerenciar failover e manter múltiplos nós adicionam overhead operacional.
- Risco de split-brain no failover automático: uma detecção incorreta de falha pode levar dois nós a se considerarem primary simultaneamente.
- Custo de infraestrutura: cada replica é um servidor adicional com custo proporcional ao primary.
Armadilhas comuns
1. Read-your-writes: ler da replica logo após escrever
Um usuário atualiza seu perfil. A escrita vai ao primary e é confirmada. O usuário é redirecionado para a página de perfil — que lê de uma replica. Se o replication lag for de 200 ms, a página exibe o perfil antigo. O usuário pensa que a atualização falhou, tenta novamente, e agora a escrita é duplicada.
Soluções: sticky reads (direcionar leituras ao primary por um período curto após uma escrita do mesmo usuário), ler do primary para operações sensíveis (perfil do usuário logado, saldos), ou transmitir o timestamp da escrita e só ler de replicas que já alcançaram aquele ponto (técnica suportada nativamente por alguns bancos).
2. Replica lag silencioso
O lag de replicação cresceu para minutos, mas a aplicação continua servindo leituras das replicas sem saber. Dashboards mostram dados de horas atrás, validações de unicidade passam em registros que já foram criados no primary, e relatórios de estoque mostram quantidades que já foram vendidas.
Monitorar o replication lag é obrigatório. Configure alertas para lag acima de um threshold aceitável para o negócio (ex.: 5 segundos). Em situações de lag alto, considere direcionar leituras ao primary temporariamente mesmo que isso aumente a carga.
3. Split-brain no failover automático
O primary sofre uma falha de rede que o torna inacessível ao orquestrador mas não ao banco de dados. O orquestrador detecta a falha e promove uma replica. Agora dois nós se consideram primary: o original (que continua recebendo escritas de clientes que ainda têm conexão com ele) e o novo (que também aceita escritas). As duas versões dos dados divergem e a reconciliação manual pode ser destrutiva.
A solução é fencing: antes de promover a replica, o orquestrador garante que o primary antigo não pode mais aceitar escritas — seja revogando suas credenciais, bloqueando sua rede ou usando STONITH (Shoot The Other Node In The Head). Nunca confie apenas na detecção de falha para evitar split-brain.
4. Queries pesadas em replicas bloqueando a replicação
Em alguns bancos (notavelmente versões antigas do MySQL com MyISAM, ou configurações específicas do PostgreSQL), queries analíticas longas numa replica podem bloquear a aplicação dos eventos de replicação vindos do primary. O lag aumenta durante a execução da query pesada. Quando a query termina, a replica precisa processar o backlog acumulado.
Solução: usar réplicas dedicadas para analytics com configurações específicas (ex.: transaction_timeout, ou isolamento via logical replication em PostgreSQL) que evitam que queries longas bloqueiem o processo de replicação.
Arquiteturas e padrões relacionados
Sharding e replicação são técnicas complementares: replicação aumenta a disponibilidade e a capacidade de leitura dentro de cada shard, enquanto sharding distribui as escritas entre múltiplos primaries. Sistemas de grande escala frequentemente combinam os dois — cada shard tem seu próprio conjunto de primary e replicas.
A distinção com escalabilidade horizontal é importante: replicação é escala horizontal de leitura, mas não de escrita. Para escalar escritas horizontalmente, sharding é necessário. Os dois conceitos são frequentemente confundidos porque ambos envolvem múltiplos nós de banco de dados.
Caching e read replicas são frequentemente usados juntos: o cache absorve as leituras mais frequentes (hit rate alto), e as replicas atendem as leituras que passam pelo cache (miss). A combinação maximiza o throughput de leitura sem sobrecarregar nenhum componente individual — e reduz os requisitos de número de replicas, diminuindo o custo de infraestrutura.