PostgreSQL: Backup, Restauração e Alta Disponibilidade no…

Última atualização: 2026-08-26

1. O Que Você Vai Aprender


2. A História

Bob é o DBA de uma plataforma de e-commerce. Às 3 da manhã, um desenvolvedor executou por engano DELETE FROM products WHERE category = 'Electronics', excluindo $2 milhões em dados de produtos.

Bob precisa recuperar para o estado das 2:59 AM — um minuto antes da exclusão equivocada — com zero perda de dados. Ele escolhe a solução PITR (Recuperação Point-in-Time).


3. Conceito: Backup Lógico

(1) Opções do pg_dump

Opção Significado Exemplo
-Fc Formato personalizado (compactado, recomendado) pg_dump -Fc db > db.dump
-Fd Formato de diretório (backup paralelo) pg_dump -Fd db -f dir/
-Fp Texto SQL simples pg_dump -Fp db > db.sql
-j N Número de tarefas paralelas (precisa de -Fd) pg_dump -Fd db -j 4 -f dir/
-t table Fazer backup apenas da tabela especificada pg_dump -t products db > p.dump
-n schema Fazer backup apenas do esquema especificado pg_dump -n public db > s.dump
--exclude-table Excluir uma tabela pg_dump --exclude-table=logs db > d.dump
-Z 0-9 Nível de compressão pg_dump -Fc -Z6 db > db.dump

▶ Exemplo: Fazer Backup de um Único Banco de Dados com pg_dump

BASH
# Formato personalizado (recomendado, compactado, restauração paralela possível)
pg_dump -h localhost -U postgres -Fc shop_db > /backup/shop_db_$(date +%Y%m%d).dump

# Formato de diretório com 4 trabalhadores paralelos
pg_dump -h localhost -U postgres -Fd shop_db -j 4 -f /backup/shop_db_dir/

# Formato SQL simples (legível, editável antes da restauração)
pg_dump -h localhost -U postgres -Fp shop_db > /backup/shop_db.sql

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

▶ Exemplo: Fazer Backup de Tabelas Específicas com pg_dump

BASH
# Fazer backup apenas das tabelas orders e order_items
pg_dump -h localhost -U postgres -Fc \
  -t orders -t order_items \
  shop_db > /backup/order_tables.dump

# Fazer backup de todas as tabelas que correspondem ao padrão
pg_dump -h localhost -U postgres -Fc \
  -t 'order_*' \
  shop_db > /backup/order_prefix_tables.dump

# Excluir tabelas de log grandes
pg_dump -h localhost -U postgres -Fc \
  --exclude-table='access_log_*' \
  shop_db > /backup/shop_no_logs.dump

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

(2) pg_dumpall vs pg_dump

Dimensão pg_dump pg_dumpall
Escopo do backup Banco de dados único Cluster inteiro (todos os bancos de dados)
Informações de roles Não incluídas Inclui definições de roles/tablespaces
Formato de saída Opcional -Fc/-Fd/-Fp Apenas SQL simples (-Fp)
Paralelo Suportado (-j) Não suportado
Cenário recomendado Backup diário de BD único Backup de roles + migração de cluster completo

▶ Exemplo: Backup de Cluster Completo com pg_dumpall

BASH
# Fazer backup de todos os bancos de dados e roles (apenas SQL simples)
pg_dumpall -h localhost -U postgres > /backup/cluster_full_$(date +%Y%m%d).sql

# Fazer backup apenas de roles (útil para migração)
pg_dumpall -h localhost -U postgres --roles-only > /backup/roles_only.sql

# Fazer backup apenas de definições de tablespace
pg_dumpall -h localhost -U postgres --tablespaces-only > /backup/tablespaces.sql

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

4. Conceito: Restauração Lógica

(1) Opções do pg_restore

Opção Significado Aplica-se ao formato
-d db Restaurar para um banco de dados especificado -Fc / -Fd
-j N Restauração paralela -Fc / -Fd
--clean DROP e depois CREATE primeiro -Fc / -Fd
--if-exists DROP IF EXISTS (com --clean) -Fc / -Fd
-t table Restaurar apenas a tabela especificada -Fc / -Fd
--list Listar conteúdo do arquivo -Fc / -Fd
--section=pre-data Restaurar apenas pré-dados (esquema) -Fc / -Fd

▶ Exemplo: Restaurar do Formato Personalizado com pg_restore

BASH
# Restaurar para um novo banco de dados
createdb -h localhost -U postgres shop_db_restore
pg_restore -h localhost -U postgres -d shop_db_restore /backup/shop_db.dump

# Restauração paralela (4 trabalhadores, formato de diretório)
pg_restore -h localhost -U postgres -d shop_db -j 4 /backup/shop_db_dir/

# Restaurar com limpeza (remover objetos existentes primeiro)
pg_restore -h localhost -U postgres -d shop_db \
  --clean --if-exists /backup/shop_db.dump

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

▶ Exemplo: Restauração Seletiva

BASH
# Listar conteúdo do arquivo para encontrar números de entrada de tabelas
pg_restore --list /backup/shop_db.dump

# Restaurar apenas tabelas específicas por nome
pg_restore -h localhost -U postgres -d shop_db \
  -t products -t categories \
  /backup/shop_db.dump

# Restaurar apenas esquema (sem dados)
pg_restore -h localhost -U postgres -d shop_db \
  --section=pre-data /backup/shop_db.dump

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

(2) pg_restore vs psql < file.sql

Dimensão pg_restore psql < file.sql
Formato de entrada -Fc / -Fd Texto SQL simples
Paralelo Suportado (-j) Não suportado
Restauração seletiva Suportada (-t / --list) Não suportada
Limpar dados antigos --clean Deve escrever DROP manualmente
Cenário recomendado Formato personalizado / diretório Saída do pg_dumpall

▶ Exemplo: Restaurar de SQL Simples

BASH
# Restaurar da saída do pg_dumpall
psql -h localhost -U postgres -f /backup/cluster_full_20250601.sql

# Restaurar do formato simples do pg_dump
createdb -h localhost -U postgres shop_db_new
psql -h localhost -U postgres -d shop_db_new -f /backup/shop_db.sql

Output:

TEXT 📖 Somente leitura
# comando psql executado com sucesso

5. Conceito: Importação/Exportação com COPY

(1) COPY vs \copy

Comando Executa em Acesso a arquivo Privilégio necessário
COPY (SQL) Lado do servidor Lê sistema de arquivos do servidor Superusuário
\copy (psql) Lado do cliente Lê arquivo do cliente Usuário normal

▶ Exemplo: Exportar para CSV com COPY

SQL
-- Exportar para CSV com cabeçalho
COPY (SELECT order_id, customer_id, total_amount, order_date
      FROM orders
      WHERE order_status = 'completed'
      ORDER BY order_date DESC)
TO '/tmp/completed_orders.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');

-- Exportar tabela inteira
COPY products TO '/tmp/products.csv' WITH (FORMAT csv, HEADER true);

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ Exemplo: Importar CSV com COPY

SQL
-- Importar de CSV
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');

-- Importar com tratamento de erro (PG 17+)
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, ON_ERROR ignore);

Output:

TEXT 📖 Somente leitura
-- instrução SQL executada com sucesso

(2) Opções de Formato do COPY

Opção Valor Descrição
FORMAT csv / text / binary Formato de saída
HEADER true / false Primeira linha são os nomes das colunas
DELIMITER ',' / '\t' Separador (vírgula padrão para csv)
QUOTE '"' Caractere de aspas
NULL '' String representando NULL
ENCODING 'UTF8' Codificação do arquivo

▶ Exemplo: Exportação/Importação em Formato Binário

SQL
-- Exportação binária (mais rápida, menor)
COPY orders TO '/tmp/orders.bin' WITH (FORMAT binary);

-- Importação binária
COPY orders FROM '/tmp/orders.bin' WITH (FORMAT binary);

Output:

TEXT 📖 Somente leitura
-- instrução SQL executada com sucesso

6. Conceito: WAL e PITR

(1) Princípio do WAL (Write-Ahead Log)

O WAL é o mecanismo central pelo qual o PG garante a integridade dos dados e suporta a recuperação.

100%
flowchart LR
    A[Cliente<br/>WRITE] --> B[Buffer WAL<br/>gravar log primeiro]
    B --> C[Arquivo WAL<br/>persistir no disco]
    B --> D[Buffer Compartilhado<br/>gravar dados depois]
    D --> E[Arquivo de Dados<br/>Checkpoint descarrega para o disco]

    style B fill:#ffcdd2
    style C fill:#ff8a80
    style D fill:#c8e6c9
    style E fill:#a5d6a7
Conceito Descrição
Segmento WAL Arquivo WAL, 16MB cada por padrão
LSN Log Sequence Number, identifica uma posição no log
Checkpoint Descarregar páginas sujas da memória para o disco, reciclar WAL
wal_level Nível de detalhe do WAL: replica / logical
archive_mode Se deve arquivar arquivos WAL
archive_command Arquivar para um caminho especificado

(2) Etapas de Configuração do PITR

Etapa Operação Descrição
1 Habilitar arquivamento WAL archive_mode = on
2 Definir comando de arquivamento archive_command = 'cp %p /archive/%f'
3 Backup base pg_basebackup
4 Arquivamento contínuo Arquivos WAL arquivados automaticamente
5 Recuperar para ponto no tempo recovery_target_time

▶ Exemplo: Configurar Arquivamento WAL

BASH
# Configurações do postgresql.conf
cat >> /etc/postgresql/16/main/postgresql.conf << 'EOF'
wal_level = replica
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/archive/%f'
archive_timeout = 300
max_wal_senders = 3
EOF

# Reiniciar o PostgreSQL
pg_ctlcluster 16 main restart

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

▶ Exemplo: Backup Base Físico com pg_basebackup

BASH
# Backup físico completo (base para PITR)
pg_basebackup -h localhost -U replicator \
  -D /backup/base_$(date +%Y%m%d) \
  -Ft -z -P \
  --checkpoint=fast

# -Ft: formato tar
# -z: compressão gzip
# -P: mostrar progresso
# --checkpoint=fast: forçar checkpoint antes do backup

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

▶ Exemplo: Recuperação Point-in-Time PITR

Bob recupera para um minuto antes da exclusão equivocada (2:59 AM):

BASH
# Etapa 1: Parar o PostgreSQL
pg_ctlcluster 16 main stop

# Etapa 2: Limpar diretório de dados existente
rm -rf /var/lib/postgresql/16/main/*

# Etapa 3: Restaurar backup base
tar -xzf /backup/base_20250601/base.tar.gz \
  -C /var/lib/postgresql/16/main/

# Etapa 4: Criar configuração de recuperação
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF

# Etapa 5: Criar arquivo de sinal de recuperação
touch /var/lib/postgresql/16/main/recovery.signal

# Etapa 6: Iniciar o PostgreSQL (irá recuperar até o tempo alvo)
pg_ctlcluster 16 main start

# O PostgreSQL irá reproduzir o WAL até 02:59:00 e então promover

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

(3) Backup Lógico vs Backup Físico vs PITR

Dimensão Backup lógico (pg_dump) Backup físico (pg_basebackup) PITR
Granularidade Tabela / BD Cluster inteiro Cluster inteiro
Precisão da recuperação Momento do backup Momento do backup Qualquer ponto no tempo
Velocidade de recuperação Lenta (SQL linha por linha) Rápida (cópia de arquivo) Média (reproduzir WAL)
Espaço de armazenamento Pequeno (compactado) Grande (cópia completa) Médio (base + arquivo)
Zero perda Não Não Sim
Backup online Sim Sim Sim

7. Conceito: Replicação e Alta Disponibilidade

(1) Replicação por Streaming vs Replicação Lógica

Dimensão Replicação por Streaming Replicação Lógica
Nível de replicação Blocos WAL físicos Alterações lógicas (INSERT/UPDATE/DELETE)
Granularidade Cluster inteiro Tabela / publicação especificada
Requisito de versão Versões do primário e standby devem corresponder Pode cruzar versões principais
Replicação de DDL Automática Não replica DDL
Destino gravável Não (standby somente leitura) Sim
Especialidade PG Replicação lógica nativa do PG (10+)

▶ Exemplo: Configurar Replicação por Streaming

BASH
# No primário: postgresql.conf
wal_level = replica
max_wal_senders = 5
wal_keep_size = '1GB'

# No primário: pg_hba.conf
echo 'host replication replicator 192.168.1.0/24 md5' >> pg_hba.conf

# No primário: criar usuário de replicação
psql -c "CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'RepP@ss';"

# No standby: fazer backup base
pg_basebackup -h primary_host -U replicator \
  -D /var/lib/postgresql/16/main -Fp -Xs -P -R

# -R: criar standby.signal e configurar automaticamente
# o standby se conectará automaticamente ao primário na inicialização

Output:

TEXT 📖 Somente leitura
# comando psql executado com sucesso

▶ Exemplo: Configurar Replicação Lógica

SQL
-- No publicador (banco de dados de origem)
CREATE PUBLICATION pub_orders FOR TABLE orders, order_items;

-- Ou publicar todas as tabelas em um esquema
CREATE PUBLICATION pub_all FOR ALL TABLES;

-- No assinante (banco de dados de destino)
CREATE SUBSCRIPTION sub_orders
  CONNECTION 'host=primary_host dbname=shop_db user=replicator password=RepP@ss'
  PUBLICATION pub_orders;

-- Verificar status da replicação
SELECT * FROM pg_stat_replication;       -- no publicador
SELECT * FROM pg_stat_subscription;      -- no assinante

Output:

TEXT 📖 Somente leitura
CREATE TABLE

(2) Visões de Monitoramento de Replicação

Visão Localização Conteúdo
pg_stat_replication Primário Status de todos os standbys conectados
pg_stat_wal_receiver Standby Status de recebimento de WAL
pg_stat_subscription Assinante Status da assinatura de replicação lógica
pg_replication_slots Primário Informações do slot de replicação

▶ Exemplo: Monitorar Atraso de Replicação por Streaming

SQL
-- Verificar atraso de replicação no primário
SELECT
  client_addr,
  state,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn,
  (sent_lsn - replay_lsn) AS replication_lag
FROM pg_stat_replication;

-- Verificar se o standby está em modo de recuperação
SELECT pg_is_in_recovery();

Output:

TEXT 📖 Somente leitura
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

8. Conceito: Estratégia de Backup

(1) Comparação Completo + Incremental

Estratégia Método Tempo de recuperação Custo de armazenamento Zero perda
Completo lógico pg_dump agendado Lento Baixo Não
Completo físico pg_basebackup agendado Rápido Alto Não
PITR Backup base + arquivo WAL Médio Médio Sim
Replicação por streaming Sincronização em tempo real do standby Mais rápido Alto Quase zero perda

▶ Exemplo: Script de Backup Automatizado

BASH
#!/bin/bash
# Script de backup diário para shop_db

BACKUP_DIR="/backup/daily"
DATE=$(date +%Y%m%d_%H%M%S)
RETAIN_DAYS=7

# Backup lógico completo (formato personalizado pg_dump)
pg_dump -h localhost -U postgres -Fc -Z6 \
  shop_db > ${BACKUP_DIR}/shop_db_${DATE}.dump

# Fazer backup das roles separadamente
pg_dumpall -h localhost -U postgres --roles-only \
  > ${BACKUP_DIR}/roles_${DATE}.sql

# Limpar backups antigos (manter 7 dias)
find ${BACKUP_DIR} -name "*.dump" -mtime +${RETAIN_DAYS} -delete
find ${BACKUP_DIR} -name "*.sql" -mtime +${RETAIN_DAYS} -delete

echo "Backup concluído: shop_db_${DATE}.dump"

Output:

TEXT 📖 Somente leitura
# comando executado com sucesso

(2) Estratégia Recomendada para Produção

Ambiente Solução recomendada RPO RTO
Desenvolvimento pg_dump diário 24h Horas
Testes pg_dump + replicação por streaming Minutos Minutos
Produção PITR + replicação por streaming Quase zero Minutos
Produção crítica PITR + replicação por streaming + replicação lógica Zero Segundos

▶ Exemplo: Verificar Integridade do Backup

BASH
# Verificar se o backup pg_dump é legível
pg_restore --list /backup/shop_db.dump > /dev/null
if [ $? -eq 0 ]; then
  echo "Backup verificado OK"
else
  echo "Backup CORROMPIDO - alertar equipe de operações!"
fi

# Verificar se o arquivo WAL não está atrasado
psql -c "SELECT pg_current_wal_lsn(), pg_last_archive_lsn();"

Output:

TEXT 📖 Somente leitura
# comando psql executado com sucesso

9. Fluxograma: Decisão de Backup/Restauração

100%
flowchart TD
    A[Precisa de backup/restauração?] --> B{Cenário de restauração?}
    B -->|Tabela/dados removidos| C{Tem backup lógico?}
    B -->|Falha total do BD| D{Tem PITR?}
    B -->|Migração planejada| E{Volume de dados?}
    C -->|Sim| F[pg_restore -t table]
    C -->|Não| G{Tem standby por streaming?}
    G -->|Sim| H[Exportar do standby]
    G -->|Não| I[Dados irrecuperáveis]
    D -->|Sim| J[PITR para o tempo alvo]
    D -->|Não| K[pg_basebackup para o momento do backup]
    E -->|Pequeno| L[pg_dump/pg_restore]
    E -->|Grande| M[pg_basebackup + streaming]
    J --> N{Versão cruzada?}
    N -->|Sim| O[Migração por replicação lógica]
    N -->|Não| P[Recuperação física]

    style I fill:#ffcdd2
    style J fill:#c8e6c9
    style O fill:#bbdefb

10. Exemplo Abrangente

Fluxo completo de recuperação PITR do Bob — recuperar para 2:59 AM após a exclusão acidental de produtos às 3h:

SQL
-- Etapa 1: Confirmar o horário do acidente e a perda de dados
SELECT pg_current_wal_lsn();  -- anotar LSN atual
SELECT COUNT(*) FROM products WHERE category = 'Electronics';  -- verificar perda

-- Etapa 2: Verificar se o arquivo WAL tem os segmentos necessários
SELECT pg_last_archive_lsn();
-- Deve ser >= LSN às 02:59 AM
BASH
# Etapa 3: Parar o PostgreSQL imediatamente para preservar o estado
pg_ctlcluster 16 main stop

# Etapa 4: Preservar arquivos WAL atuais (NÃO excluir!)
cp -r /var/lib/postgresql/16/main/pg_wal /tmp/pg_wal_backup/

# Etapa 5: Restaurar backup base
rm -rf /var/lib/postgresql/16/main/*
tar -xzf /backup/base_20250531/base.tar.gz \
  -C /var/lib/postgresql/16/main/

# Etapa 6: Copiar WAL preservado de volta para recuperação completa
cp /tmp/pg_wal_backup/* /var/lib/postgresql/16/main/pg_wal/

# Etapa 7: Configurar alvo PITR
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF

touch /var/lib/postgresql/16/main/recovery.signal

# Etapa 8: Iniciar e verificar
pg_ctlcluster 16 main start

# Etapa 9: Verificar recuperação
psql -c "SELECT COUNT(*) FROM products WHERE category = 'Electronics';"
SQL
-- Etapa 10: Após recuperação bem-sucedida, fazer um novo backup
-- Executar no psql após verificar a integridade dos dados
SELECT pg_switch_wal();  -- forçar troca de WAL para ponto de arquivo limpo
BASH
# Etapa 11: Fazer novo backup base para PITR futuro
pg_basebackup -h localhost -U postgres \
  -D /backup/base_$(date +%Y%m%d) -Ft -z -P

❓ Perguntas Frequentes

P: O pg_dump bloqueia a tabela ao fazer backup? R: O pg_dump usa um snapshot (MVCC), então não bloqueia tabelas e não bloqueia leituras/escritas. Mas ele faz backup do snapshot de dados no momento inicial; novos dados gravados durante o backup não são incluídos.

P: O PITR pode recuperar até um momento antes de uma única linha ser excluída? R: Sim, desde que o arquivo WAL cubra aquele período. A granularidade mais fina do PITR é o nível de transação — recovery_target_xid pode apontar uma transação específica. Mas você não pode pular algumas transações e reverter apenas operações específicas.

P: Arquivos de arquivo WAL podem ser excluídos? R: WAL que passou de um Checkpoint e não é mais necessário por um standby pode ser excluído. Mas a recuperação PITR precisa de todos os segmentos WAL desde o backup base até o tempo alvo, então confirme que não são mais necessários antes de excluir.

P: Um standby de replicação por streaming pode atender consultas de leitura? R: Sim. Com hot_standby = on, o standby aceita consultas somente leitura (SELECT), chamado de "réplica de leitura", e é bom para descarregar carga de consultas analíticas.

P: A replicação lógica pode cruzar versões do PostgreSQL? R: Sim. A replicação lógica envia alterações lógicas (operações SQL), não dependendo do formato físico, então suporta atualizações e migrações entre versões principais. Esta é uma vantagem importante da replicação lógica do PG.

P: Qual escolher, pg_basebackup ou pg_dump? R: Escolha pg_basebackup (backup físico) se você precisa de PITR ou recuperação de cluster completo; escolha pg_dump (backup lógico) para restauração seletiva, migração entre versões ou exportação de tabela única. Produção geralmente usa ambos.

P: O que é mais rápido para importar, COPY ou INSERT? R: COPY é muito mais rápido. COPY é uma escrita em massa com uma única viagem de protocolo; INSERT analisa e executa linha por linha. Prefira COPY para grandes importações de dados; INSERT é mais flexível para pequenas quantidades.

P: O que significa um erro de pg_restore --list no script de backup? R: O arquivo de backup pode estar corrompido ou incompleto. --list apenas lê o diretório do arquivo sem realizar a restauração, então é uma maneira rápida de verificar a integridade do backup. Se estiver corrompido, restaure de outro backup.


📖 Resumo


📝 Exercícios

  1. ⭐ Faça backup do banco de dados shop_db com pg_dump -Fc, depois verifique a integridade do backup com pg_restore --list e restaure-o no banco de dados shop_db_test.

  2. ⭐⭐ Escreva um script de backup automatizado: um backup completo diário com pg_dump (formato personalizado, compactado), retendo 7 dias, verificando com pg_restore --list após o backup. Também exporte a tabela orders para CSV (com cabeçalho) usando COPY, nomeado por data.

  3. ⭐⭐⭐ Configure uma solução PITR completa: habilite o arquivamento WAL (archive_command para um diretório especificado), execute um backup base pg_basebackup, simule a exclusão de dados por engano e então recupere para um ponto no tempo especificado. Registre o LSN e o tempo de recuperação e verifique a integridade dos dados. Adicionalmente, configure um standby de replicação por streaming e verifique se as consultas de leitura funcionam.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%