System Design

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.