O original e as fotocópias da biblioteca
A biblioteca tem um único exemplar original que pode ser corrigido e anotado — e várias fotocópias espalhadas pelas salas para quem só quer ler. Cem pessoas leem as cópias ao mesmo tempo sem incomodar quem cuida do original. Quando o original muda, as cópias são refeitas — e há um intervalo em que uma sala ainda tem a versão antiga. É exatamente o trade-off da réplica: leitura barata e em paralelo, ao preço de um atraso até a cópia alcançar o original.
O atraso que muda tudo
A réplica não é o primário no mesmo instante: ela recebe o log de mudanças (o WAL, no Postgres) e o aplica com um atraso — normalmente milissegundos, mas que cresce sob carga de escrita.
Esse lag é a mesma janela da consistência eventual, e o caso que mais dói é o mesmo: ler da réplica logo depois de escrever no primário devolve o valor antigo. Roteamento cego 'todo SELECT vai à réplica' quebra exatamente aí.
Há uma sutileza que engana muita gente: nem todo SELECT é uma leitura pura. Um SELECT saldo ... FOR UPDATE, ou qualquer consulta cujo resultado decide uma escrita seguinte (checar estoque para depois debitar), precisa do valor atual e do lock — então vai ao primário, apesar de começar com SELECT. Rotear por 'a query começa com SELECT?' é uma heurística que falha justamente nas operações críticas.
A relação com CQRS costuma confundir. Réplica de leitura é infraestrutura: o mesmo esquema, a mesma query, só que servida de outro nó. CQRS é modelagem: um modelo de escrita e um modelo de leitura diferentes, muitas vezes em tecnologias diferentes (o transacional grava, o Elasticsearch serve a busca). Réplica é o degrau anterior e mais barato; CQRS é a mudança de modelo que você faz quando espelhar o mesmo esquema não basta mais.
Sob falha, a réplica introduz um modo de erro próprio: se a replicação para e ninguém percebe, as consultas continuam respondendo — com dado cada vez mais velho. Diferente de um banco que cai e grita, a réplica atrasada mente em silêncio. Por isso o lag de replicação é uma métrica de primeira classe: sem alerta sobre ele, a divergência só aparece quando um usuário reclama.
Como isso aparece na prática
O relatório que derrubava o checkout
Cenário
Todo início de mês, a equipe financeira rodava um relatório pesado que varria meses de pedidos. Enquanto ele rodava, a latência do checkout subia e algumas compras expiravam por timeout.
Como o padrão entra
O relatório e as demais consultas analíticas passaram a rodar contra uma réplica dedicada, deixando o primário exclusivamente para o caminho transacional do checkout.
Trade-off: O relatório passou a enxergar dados com alguns segundos de atraso em relação ao primário — irrelevante para um fechamento mensal, e um ótimo negócio diante do checkout protegido.
O painel que mostrava o pedido inexistente
Cenário
Após criar um pedido, o usuário era redirecionado para o painel, que lia da réplica. Às vezes o pedido recém-criado ainda não tinha replicado, e o painel exibia 'nenhum pedido encontrado'.
Como o padrão entra
A leitura imediatamente após a criação passou a ir ao primário por alguns segundos (read-your-writes), enquanto as visitas posteriores ao painel seguiram na réplica.
Trade-off: Uma fração pequena das leituras voltou ao primário logo após cada escrita. É um custo mínimo perto de um usuário achando que o pedido que ele acabou de fazer sumiu.
Onde isto dá errado
Rotear por 'começa com SELECT'
Uma consulta que lê para decidir uma escrita — checar estoque, ler saldo com FOR UPDATE — precisa do valor atual e do lock, e tem que ir ao primário. A heurística de rotear todo SELECT para a réplica falha exatamente nas operações que menos podem falhar.
Ignorar o lag de replicação
Réplica que para de replicar não cai: continua respondendo com dado cada vez mais velho, em silêncio. Sem alerta sobre o lag, a divergência só aparece quando um usuário reclama de um valor que 'sumiu'.
Esperar que réplica escale escrita
Toda escrita continua indo ao único primário; a réplica só divide leitura. Se o gargalo é escrever, adicionar réplicas não move o ponteiro — o caminho passa a ser sharding.
Ler da réplica logo depois de escrever
É o read-your-writes de novo: o usuário acabou de gravar e a leitura seguinte cai numa réplica atrasada, mostrando o valor antigo. A janela é curta, mas é onde o usuário mais tem certeza de qual valor deveria aparecer.
Um primário, muitas réplicas
Roteando escrita e leitura
// Uma pool para escrever, outra para ler. A regra e simples de
// enunciar e facil de errar: escrita e "read-your-writes" vao ao
// primario; o resto vai a replica.
const primario = criarPool(process.env.DB_PRIMARIO);
const replica = criarPool(process.env.DB_REPLICA); // pode ser N replicas
async function executar(sql: string, params: unknown[], opts = { escrita: false }) {
const pool = opts.escrita ? primario : replica;
return pool.query(sql, params);
}
// Consulta pesada de relatorio: replica, sem tocar no primario.
await executar("SELECT ... FROM pedidos WHERE ...", [], { escrita: false });
// Debito: primario, sempre.
await executar("UPDATE contas SET saldo = saldo - $1 WHERE id = $2", [v, id], {
escrita: true,
});
// A pegadinha: um SELECT que decide uma escrita NAO e leitura pura.
// Ele precisa do valor atual, entao vai ao primario tambem.Quando usar × quando evitar
Use quando
3- Carga de leitura muito maior que a de escrita.
- Isolar consultas pesadas do caminho transacional.
- Primeiro degrau de escala, antes do sharding.
Evite quando
2- Quando o gargalo é a escrita.
- Quando nenhuma leitura tolera o atraso de replicação.
Testar o que ficou
4 armadilhas de Réplica de leitura viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.
Onde isto aparece de verdade
Você já usa isto, talvez sem saber o nome.
Read Replica do Amazon RDSUm clique cria uma cópia somente-leitura que recebe a replicação do primário e absorve as consultas mais pesadas.hot standby do PostgresA réplica de streaming aplica o WAL do primário e serve SELECTs enquanto isso, aliviando o nó de escrita.endpoint de leitura separadoA aplicação manda escritas para um host e leituras para outro — a divisão física entre gravar e consultar.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Escrita no primario; leitura na replica.
const pool = opts.escrita ? primario : replica;O que ele cobra
1 salto a mais entre a chamada e o efeito.
- Introduz um atraso de replicação: a réplica vê o passado até o WAL do primário chegar e ser aplicado
- Obriga o código a rotear cada consulta, e a saber que um SELECT que decide uma escrita não é leitura pura
- Falha de replicação vira dado divergente silencioso — precisa de alerta sobre o lag, senão ninguém percebe
Não vale se: o gargalo é a escrita, não a leitura. Réplica não divide carga de escrita — para isso o caminho é sharding, que reparte as próprias linhas.
Onde isto aparece
Continue por aqui
- CQRS (Command Query Responsibility Segregation)Separa o modelo de escrita (commands) do modelo de leitura (queries), podendo usar stores e serviços distintos otimizados para cada lado.
- Consistência eventualQuando o dado vive em cópias, mantê-las idênticas a cada instante custa latência e disponibilidade. A consistência eventual troca o 'sempre igual' por 'igual daqui a pouco': as réplicas convergem, e o sistema fica mais rápido e mais disponível — ao preço de janelas em que a leitura vê o passado.
- ShardingQuando os dados não cabem mais numa máquina — nem em disco, nem em escrita —, a saída é reparti-los: cada shard guarda um pedaço, escolhido por uma chave. Escala escrita e volume de forma horizontal, ao preço alto de perder as junções fáceis, as transações entre shards e a chance de errar a chave e nunca mais conseguir corrigir barato.