Tecnologias/02 - Bancos de Dados/01 - Relacionais/01 - PostgreSQL6 min
WAL, Checkpoint e Replicação
Conceito
O WAL (Write-Ahead Log) resolve um problema aparentemente impossível: como garantir durabilidade sem escrever no disco, de forma aleatória, todas as páginas que uma transação tocou?
A resposta é uma inversão:
Não grave a mudança onde o dado mora. Grave a intenção num log sequencial, e confirme o
COMMITquando o log estiver seguro. As páginas de dados vão para o disco depois, com calma.
COMMIT
↓
1. escreve o registro no WAL (arquivo sequencial, append-only)
2. fsync do WAL ← É AQUI que o COMMIT termina
3. responde "ok" ao cliente
...
4. minutos depois: background writer / checkpointer gravam as páginas de dados
Uma escrita sequencial de alguns bytes substitui N escritas aleatórias de 8 KB. Se o servidor cair entre 3 e 4, o WAL é reexecutado (replay) na inicialização e nada é perdido. É o mesmo princípio do redo log do InnoDB e do Oracle — e, curiosamente, o mesmo princípio do log do Kafka.
LSN: o endereço de tudo
Cada registro do WAL tem um LSN (Log Sequence Number), tipo 0/16B3B48 — um offset
monotônico no fluxo de WAL. Ele é a régua universal do Postgres:
- cada página de dados guarda o LSN da última mudança que a afetou;
- a réplica reporta "apliquei até o LSN X" (é assim que se mede lag em bytes);
- PITR usa LSN/timestamp como alvo de recuperação.
SELECT pg_current_wal_lsn(); -- onde o primário está
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) -- lag em BYTES, não em segundos
FROM pg_stat_replication;
Checkpoint: a dívida que precisa ser paga
O WAL só pode ser descartado depois que as páginas correspondentes chegarem ao disco. O checkpoint é esse acerto de contas:
- Marca um ponto no WAL.
- Força todas as páginas sujas do
shared_bufferspara o disco. - Grava o
pg_controldizendo "a recuperação pode começar daqui".
Dois gatilhos: tempo (checkpoint_timeout, padrão 5 min) e volume (max_wal_size).
| Checkpoint mais frequente | Checkpoint mais espaçado |
|---|---|
| recuperação rápida após crash | recuperação mais longa |
| picos de I/O frequentes | menos I/O total (uma página suja 10× é escrita 1×) |
| menos WAL acumulado | mais espaço em pg_wal |
O ajuste padrão para OLTP é espaçar (max_wal_size grande) e espalhar
(checkpoint_completion_target = 0.9, que dilui a escrita ao longo de 90% do intervalo).
full_page_writes e a amplificação silenciosaNa primeira modificação de uma página após um checkpoint, o Postgres grava a página
inteira (8 KB) no WAL, não só o delta — proteção contra torn page (escrita
parcial). Por isso o volume de WAL sobe muito logo após cada checkpoint, e por isso
wal_compression = on costuma valer a pena.
Replicação: dois mecanismos diferentes
Física (streaming) — a réplica recebe o WAL bruto e o reexecuta:
primário ──[WAL bytes]──> walsender → walreceiver → replay na réplica
- Cópia byte a byte: todo o cluster, mesma versão major, réplica somente leitura.
- Baixíssima latência; é o que RDS/Aurora e Patroni usam.
synchronous_commit = on|remote_apply|offdecide se oCOMMITespera a réplica.
Lógica — a réplica recebe mudanças de linha decodificadas (via logical decoding):
primário → slot de replicação → plugin (pgoutput) → INSERT/UPDATE/DELETE lógicos
- Por tabela, entre versões diferentes, para outro Postgres ou para Kafka/Debezium.
- É o mecanismo de CDC e a base do outbox automatizado (1. Mensageria e Desacoplamento).
- Custo: precisa de PK ou
REPLICA IDENTITY, não replica DDL, e o slot é perigoso.
| Física | Lógica | |
|---|---|---|
| Granularidade | cluster inteiro | tabela |
| Réplica escrevível | não | sim |
| Versões diferentes | não | sim |
| Uso típico | HA, réplica de leitura | CDC, migração, integração |
| DDL | replicado (é byte) | não |
Replication slots: o pé no freio que enche o disco
Um slot garante que o primário não descarte WAL que um consumidor (réplica ou Debezium) ainda não leu. É exatamente o que você quer — e é o modo mais rápido de encher o disco do primário:
Consumidor lógico parado + slot ativo =
pg_walcrescendo até 100% do disco = banco parado.
Sempre configure max_slot_wal_keep_size (Postgres 13+) para que o slot seja invalidado
antes de o disco morrer. Perder a réplica é ruim; perder o primário é pior.
Trade-offs
synchronous_commit é o botão durabilidade × latência. Com off, o COMMIT não
espera o fsync do WAL: você ganha muito throughput e aceita perder até
wal_writer_delay de transações num crash (sem corromper nada — só perde as últimas).
Para telemetria, é uma troca ótima. Para financeiro, não.
Replicação síncrona multiplica a latência e pode parar o primário. Com
synchronous_standby_names e uma única réplica síncrona, se a réplica cair o primário
para de aceitar escrita (ele está esperando alguém confirmar). O desenho correto é
ANY 1 (r1, r2) com duas réplicas — senão você transformou HA em ponto único de falha.
É Availability vs Consistency em uma linha de configuração.
PITR é a única defesa contra erro humano. Réplica não protege de DELETE sem
WHERE: o comando é replicado em milissegundos. Só backup base + WAL archive permite
voltar a "10 segundos antes do incidente".
O trade-off central: o WAL transforma escrita aleatória em sequencial, e o preço é que tudo passa a depender do WAL: durabilidade, recuperação, réplicas e CDC. Quem não gerencia o volume, o arquivamento e os slots do WAL não gerencia o Postgres.
Exemplo prático
Arquivamento e PITR — o mínimo para um banco de produção:
wal_level = replica # 'logical' se você for usar CDC/Debezium
archive_mode = on
archive_command = 'pgbackrest --stanza=prod archive-push %p'
max_wal_size = 4GB
checkpoint_completion_target = 0.9
wal_compression = on
max_slot_wal_keep_size = 64GB # o slot morre antes do disco
# recuperar até 1 minuto antes do DELETE fatídico
pgbackrest --stanza=prod --type=time \
--target="2026-09-08 14:31:00" --target-action=promote restore
Monitoramento essencial de replicação:
-- lag por réplica, em bytes e em tempo
SELECT client_addr, state, sync_state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag_bytes,
replay_lag
FROM pg_stat_replication;
-- slots: 'active = false' com lag alto é o alerta que salva o disco
SELECT slot_name, plugin, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_retido
FROM pg_replication_slots ORDER BY 4 DESC;
Como isso falha em produção
| Sintoma | Causa |
|---|---|
pg_wal enchendo o disco |
slot inativo, ou archive_command falhando (o WAL não pode ser descartado) |
| Primário parou de aceitar escrita | única réplica síncrona ficou indisponível |
| Latência de escrita com picos de 30 s | checkpoint concentrado; max_wal_size pequeno |
| Réplica cancelando queries longas | conflito entre replay e snapshot: hot_standby_feedback ou max_standby_streaming_delay |
| Réplica não acompanha nunca | replay é single-threaded; escrita do primário maior que o I/O da réplica |
| CDC "pulou" mudanças | slot recriado, ou wal_level não era logical |
| Restore não funciona | ninguém testou o restore. Backup não testado não é backup. |
Aprofundamento aplicado
Confirmar, replicar e aplicar são marcos diferentes
Uma gravação pode estar confirmada no primário e ainda não estar visível em uma réplica. A réplica precisa receber WAL, persistir conforme a garantia exigida e reaplicar os registros para expor o estado correspondente. O nível de confirmação síncrona escolhido influencia qual desses marcos participa da espera do commit. Referência: replicação síncrona.
Cenário: o cliente cria um pedido e é direcionado a uma réplica para consultá-lo. Uma resposta “não existe” pode refletir atraso, e não perda. Opções incluem ler no primário nessa jornada ou coordenar a leitura com a posição necessária, conforme a arquitetura.
Dimensionar retenção pelo ritmo de escrita
Se o WAL cresce a 20 MiB/s e um consumidor fica parado por uma hora, a ordem de grandeza do volume produzido é 72.000 MiB, antes de considerar outros limites e causas de retenção. Uma política de slots precisa ligar atraso, espaço disponível e estratégia de ressincronização.
Exercício: desenhe a falha do primário antes e depois de a réplica persistir o WAL. Resultado esperado: explique quais confirmações podem se perder na configuração assíncrona e por que promover uma réplica exige impedir escritas no antigo primário.
Relacionado
- 2. Arquitetura de Processos e Memória
- 4. MVCC, Tuplas e VACUUM
- 7. Diagnóstico e Tuning
- 3. Redo, Undo e Locks
- Replication
- RTO e RPO
- Availability vs Consistency
Parte de MOC PostgreSQL · WAL (docs)