postgres postgresql backup

Backup

Um backup é uma foto física (baseada em arquivos, diretórios e links simbólicos) da instância inteira do PostgreSQL, que inclui todos os bancos de dados e objetos globais, assim como o estado das transações e a posição da instância na linha do tempo. Com isso, ele pode ser usado para restaurar a instância para qualquer momento entre o final do backup e a última transação processada (contanto que o histórico da linha do tempo seja alimentado sem lacunas na restauração).

Um backup (e sua respectiva restauração) é uma estratégia usada para garantir a continuidade do seu negócio mesmo após grandes falhas de hardware e software, assim como erros humanos acidentais ou ações maliciosas. Ele também é o ponto de partida das replicações físicas (síncronas e assíncronas).

ATENÇÃO! Dumps não são propriamente backups e não devemos depender deles para fins de confiabilidade ou continuidade de negócio porque além da grande quantidade de locks que eles exigem para serem extraídos e do alto tempo de restauração necessário para restaurá-los, eles também não atendem a maioria dos casos em que temos RPO (Recovery Point Objective), causando uma perda de dados maior que o aceitável.

Existem diversas formas de fazer um backup do PostgreSQL. A mais comum é no nível do sistema de arquivos, através de ferramentas comuns, como cp. Também é possível usar snapshots de volumes ou de sistemas de arquivos.

Contudo, algumas características que todas têm em comum incluem o fato de que o sistema continua disponível para transações a todo momento, sem locks adicionais serem adquiridos. Adicionalmente, backups podem ser usados para restauração do estado da instância para qualquer momento após ele ter sido feito, como é necessário para recuperação de desastres e criação de réplicas, contanto que todo o histórico de WAL esteja sendo continuamente guardado com segurança e confiabilidade, como visto em WAL.

Estratégias

Os passos simplificados para criar um backup são os seguintes:

  1. O comando de início do backup (pg_start_backup()) coloca uma marcação (A) na linha do tempo do WAL indicando que um backup foi iniciado (no segmento …012).
  2. Os diretórios de dados são copiados (2), incluindo o diretório de dados principal e todos os diretórios de tablespaces.
  3. O comando de fim de backup (pg_stop_backup()) coloca outra marcação (B) na linha do tempo do WAL indicando que o backup terminou (no segmento …013).
  4. Todos os segmentos de WAL gerados entre as duas marcações, incluindo aqueles nas quais as próprias marcações se encontram, são copiados e guardados com os diretórios de dados. Eles podem ser copiados por arquivamento (a), por conexão de replicação (b).
                                   ╔═══════════╗↔──────────────────┐
                                   ║ Aplicação ║╗↔─────────────────┤
                                   ╚═══════════╝║╗↔────────────────┤
                                    ╚═══════════╝║↔────────────────┤
                                     ╚═══════════╝↔────────────────┤
                                                                   │
                     ┌───────────────────┐                         │
╔════════════════════╡PostgreSQL (origem)╞═════════════════════╗   │
║                    └───────────────────┘                     ║   │
╠═╡global/╞═══════╗                     ╔══════════════╡base/╞═╣   │
║                 ║                     ║                      ║   │
║     O     O     ║                     ║ ┏━┥banco de dados┝━┓ ║   │
║    ─┼─   ─┼─    ║                     ║ ┃ ┌─┤schema├─────┐ ┃ ║   │
║     │     │     ║                  ┊←┈╢ ┃ │ ☑ tabelas    │←╂─╫───┤
║    ╱ ╲   ╱ ╲    ║                  ┊←┈╢ ┃ │ ☑ índices    │←╂─╫───┤
║ ☑ usuários      ║                  ┊←┈╢ ┃ │ ☑ visões     │←╂─╫───┤
║ ☑ grupos        ║                  ┊←┈╢ ┃ │ ☑ sequências │←╂─╫───┤
║ ☑ slots de      ║                  ┊←┈╢ ┃ │ ☑ …          │←╂─╫───┘
║ ☑ replicação    ║                  ┊  ║ ┃ └──────────────┘ ┃ ║
║ ☑ pg_control┅┅┅┅╫┅┅┓               ┊  ║ ┗━━━━━━━━━━━━━━━━━━┛ ║
║                 ║  ┋               ┊  ║                      ║
╠═════════════════╝  ┋               ┊  ╚══════════════════════╣
║                    ┋               ↓                         ║
╠═╡pg_wal/╞══════════╪═══════════════╪═══════════╗             ║
║                    ┋               ┊┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┐
║  ┌────────────┐ ┌──↓─────────┐     ↓           ║     (4)     ║   (b)   ┊
║ →│+--++--(A)++│→│-++++-(B)--+│→╷++-.........╷→ ║             ║         ┊
║  └┤/…00000012├┘ └┤/…00000013├┘↓└┤/…00000014├┘  ║  ┌───────┐  ║  (a)    ┊
║                               └┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈→╫→┈│…(B)--+│┈→╫→┈┈┐     ┊
║                                                ║  └┤/…013├┘  ║   ┊     ┊
╚════════════════════════════════════════════════╩═════════════╝   ┊     ┊
                                             ╏                     ┊     ┊
                                             ╏                     ┊     ┊
                                             ╏                     ┊     ┊
                                             ╏                     ↓     ↓
                                             ╏       ┏━━━━━━━━━━━━━┷━━━━━┷━━━━━━━┓
                                          (2)╏       ┃    ┌───────┐ ┌───────┐    ┃
                                         ╔═══════╗   ┃   →│…-(A)++│→│…(B)--+│→   ┃
                                         ╠══╗ ╔══╣   ┃    └┤/…012├┘ └┤/…013├┘    ┃
                                         ╠══╝ ╚══╣   ┠────────↑────────↑─────────┨
                                         ╠═════╗ ║   ┃     ╔═══════════════╗     ┃
                                         ╚═════╩═╝   ┃     ╠═════╗   ╔═════╣     ┃
                                             ╏       ┃     ║     ║   ║     ║     ┃
                                             ┗╍╍╍╍╍╍╸┃……………╠═════╝   ╚═════╣……………┃
                                                     ┃     ╠══════════╗    ║     ┃
                                                     ┃     ║          ║    ║     ┃
                                                     ┃     ╚══════════╩════╝     ┃
                                                     ┃    ┌──────────────────┐   ┃
                                                     ┗━━━━┥Servidor de backup┝━━━┛
                                                          └──────────────────┘

Quando o backup for restaurado, a instância estará em um estado inconsistente pois a cópia do segundo passo não foi atômica e, portanto, os arquivos estão espalhados em momentos distintos da linha do tempo do WAL. Para alcançar um estado consistente o PostgreSQL irá reaplicar todas as alterações contidas no WAL entre o início e o fim do backup, marcados pelos passos um e três.

Portanto, o último passo é necessário para guardar ao menos os segmentos gerados durante o backup, caso contrário não é possível encontrar um estado consistente e o PostgreSQL vai se recusar a aceitar conexões. Se o arquivamento já tiver sido configurado, o quarto passo pode ser omitido.

Ao final, o backup resultante é quase indistinguível de uma instância parada, sendo que para restaurá-lo só é necessário iniciar o serviço. Também é possível comprimir, encriptar, deduplicar, mover e copiar o diretório de backup como qualquer outro diretório com arquivos.

É possível seguir esses passos usando a API de baixo nível, Mas essa seria uma tarefa manual repleta de detalhes e riscos que só deveriam ser enfrentados pelas ferramentas de backup.

Backup por pg_basebackup

A ferramenta trazida pelo próprio PostgreSQL é o pg_basebackup. Ele engloba todos os passos acima, inclusive com o arquivamento por conexão de replicação nas versões recentes. Quando executado, ele gera um diretório de dados contendo uma cópia fiel da instância original e incluindo os segmentos de WAL necessários para a restauração:

[postgres@pg-1 ~]$ pg_basebackup -D meubackup
[postgres@pg-1 ~]$ ls meubackup/pg_wal
000000010000007F00000094  archive_status

Mas se o arquivamento estiver já estiver sendo feito de outra forma, ele pode ser omitido do backup com -X none:

[postgres@pg-1 ~]$ pg_basebackup -D meubackup -X none
[postgres@pg-1 ~]$ ls meubackup/pg_wal
archive_status

Backup por snapshot de volume

Alguns sistemas de arquivos, gerenciadores de volumes, virtualizadores e storages têm a capacidade de fazer snapshots dos seus conteúdos. Dado que a todo momento ou o estado do PostgreSQL em disco é consistente ou a consistência pode ser alcançada através do arquivo de controle e diretório de WAL, é possível usar essa funcionalidade como estratégia de backups contanto que o PostgreSQL esteja completamente em um volume, sem tablespaces adicionais ou separação de pg_wal em outro volume.

Restauração

Para restaurar um backup, é necessário:

  1. Movê-lo (e descompactar, desencriptar) para a localização onde o serviço possa alcançá-lo, normalmente copiando do servidor de backup para o servidor de banco de dados.
  2. Todo o WAL gerado entre as marcações de início (A) e fim (B) do backup precisa estar disponível na restauração. Os segmentos podem ser copiados junto com o backup e colocados em pg_wal (a) ou recuperados do arquivamento (b).
  3. Ao iniciar o serviço, o PostgreSQL irá reproduzir todas as alterações entre (A) e (B) para alcançar um estado consistente.
  4. Dependendo do alvo e da ação escolhidas, a instância pode começar a responder transações da aplicação.
                                   ╔═══════════╗↔────────────────────────┐
                                   ║ Aplicação ║╗↔───────────────────────┤
                                   ╚═══════════╝║╗↔──────────────────────┤
                                    ╚═══════════╝║↔──────────────────────┤
                                     ╚═══════════╝↔──────────────────────┤
                                                                      (4)│
                                                                         │                    ┌───────────────────────┐
                                                                         │   ╔════════════════╡PostgreSQL (restaurado)╞═════════════════════╗
                                                                         │   ║                └───────────────────────┘                     ║
                                                                         │   ╠═╡base/╞══════════════╗                     ╔═══════╡global/╞═╣
                                                                         │   ║                      ║                     ║                 ║
                                                                         │   ║ ┏━┥banco de dados┝━┓ ║                     ║     O     O     ║
                                                                         │   ║ ┃ ┌─┤schema├─────┐ ┃ ║                     ║    ─┼─   ─┼─    ║
                                                                         ├───╫─╂→│ ☑ tabelas    │ ┃ ╟←┈┊(3)               ║     │     │     ║
                                                                         ├───╫─╂→│ ☑ índices    │ ┃ ╟←┈┊                  ║    ╱ ╲   ╱ ╲    ║
                                                                         ├───╫─╂→│ ☑ visões     │ ┃ ╟←┈┊                  ║ ☑ usuários      ║
                                                                         ├───╫─╂→│ ☑ sequências │ ┃ ╟←┈┊                  ║ ☑ grupos        ║
                                                                         └───╫─╂→│ ☑ …          │ ┃ ╟←┈┊                  ║ ☑ slots de      ║
                                                                             ║ ┃ └──────────────┘ ┃ ║  ┊                  ║ ☑ replicação    ║
                                                                             ║ ┗━━━━━━━━━━━━━━━━━━┛ ║  ┊               ┏┅┅╫┅☑ pg_control    ║
                                                                             ║                      ║  ┊               ┋  ║                 ║
                                                                             ╠══════════════════════╝  ┊               ┋  ╚═════════════════╣
                                                                             ║                         ↑               ┋                    ║
                                                                             ║             ╔═══════════╪═══════════════╪══════════╡pg_wal/╞═╣
                                                                             ║             ║           ┊(2)            ┋                    ║
                                                                             ║             ║           ↑     ┌─────────↓──┐ ┌────────────┐  ║
                                                                             ║     (b)     ║ ←╷.........-++╷←│+--(B)-++++-│←│++(A)--++--+│← ║
                                                                             ║  ┌───────┐  ║  └┤/…00000014├┘↑└┤/…00000013├┘ └┤/…00000012├┘  ║
                                                                         ┌┈┈→╫→┈│…(B)--+│┈→╫→┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘                               ║
                                                                         ┊   ║  └┤/…013├┘  ║                   (a)                          ║
                                                                         ┊   ╚═════════════╩════════════════════════════════════════════════╝
                                                                         ┊               ╏
                                                                         ┊               ╏
                                                                         ┊               ╏
                                                                         ↑               ╏
                                                     ┏━━━━━━━━━━━━━━━━━━━┷━━━━━━━┓       ╏(1)
                                                     ┃    ┌───────┐ ┌───────┐    ┃       ╏
                                                     ┃   →│…-(A)++│→│…(B)--+│→   ┃   ╔═══════╗
                                                     ┃    └┤/…012├┘ └┤/…013├┘    ┃   ╠══╗ ╔══╣
                                                     ┠────────↑────────↑─────────┨   ╠══╝ ╚══╣
                                                     ┃     ╔═══════════════╗     ┃   ║ ╔═════╣
                                                     ┃     ╠═════╗   ╔═════╣     ┃   ╚═╩═════╝
                                                     ┃     ║     ║   ║     ║     ┃       ╏
                                                     ┃……………╠═════╝   ╚═════╣……………┃╺╍╍╍╍╍╍┛
                                                     ┃     ╠══════════╗    ║     ┃
                                                     ┃     ║          ║    ║     ┃
                                                     ┃     ╚══════════╩════╝     ┃
                                                     ┃    ┌──────────────────┐   ┃
                                                     ┗━━━━┥Servidor de backup┝━━━┛
                                                          └──────────────────┘

Recuperando WAL arquivado

Para isso, é necessário fornecer os segmentos direta ou indiretamente ao PostgreSQL antes que o serviço seja iniciado sobre o diretório de dados. A forma direta é copiando manualmente os segmentos para o diretório pg_wal, que serão consumidos naturalmente. Contudo, não é prático fazer essa cópia completa, especialmente se o volume de segmentos for muito alto.

A melhor forma é informando ao PostgreSQL um comando similar ao archive_command, mas com o propósito oposto, ou seja, de buscar um segmento de WAL de um local remoto. O parâmetro que contém o comando é chamado restore_command, aceita os padrões %f para nome do arquivo e %p para caminho absoluto até o diretório onde o arquivo será armazenado, por exemplo:

restore_command = 'scp pg-a.local:archive/16/%f %p'

A partir do PostgreSQL versão 12, esse parâmetro é colocado no arquivo postgresql.conf. Em versões mais antigas, ele é colocado em um arquivo recovery.conf. Outros parâmetros de restauração também acompanham essa movimentação.

Por fim, a partir da versão 12 é necessário criar um arquivo recovery.signal para indicar que o PostgreSQL deve iniciar a recuperação. Em versões mais antigas é necessário adicionar a configuração standby_mode = on no arquivo recovery.conf.

Alvo da recuperação

Em alguns casos queremos restaurar o backup até certo ponto do passado, por exemplo antes de um DROP acidental. Para isso, podemos incluir um alvo na recuperação. Quando esse alvo é alcançado, o PostgreSQL toma uma ação (recovery_target_action), como pausar a instância (pause), permitindo consultas e inspeção, parar o serviço (shutdown) ou mesmo promover a instância torna-a um novo primário (promote).

Esse alvo é informado como mais alguns parâmetros no arquivo de configuração e ele pode ser um xid (recovery_target_xid), um LSN (recovery_target_lsn), um nome definido previamente (recovery_target_name) ou uma data e hora específicos (recovery_target_time). Por exemplo, o DBA pode ter criado um ponto de restauração antes de uma migração arriscada:

[[local]:5432] postgres@postgres=# SELECT pg_create_restore_point('pre-migracao');

Quando um backup daquela instância precisar avançado até aquele ponto e restaurado como novo primário, o DBA usa o alvo e a ação:

recovery_target_name = 'pre-migracao'
recovery_target_action = 'promote'

A restauração com alvo é também chamada de Point-in-Time Recovery (PITR), ou restauração a um ponto no tempo.

Ferramentas de terceiros

Apesar do pg_basebackup fazer backups confiáveis, ele não cuida de diversas outras tarefas importantes, como:

Para essas e outras funcionalidades, é necessário usar ferramentas de terceiros, como:

Prática

  1. Faça um backup com pg_basebackup da instância de pg-1 para um local na máquina pg-a.
  1. Copie o backup para a máquina pg-2.
  1. Restaure o backup na máquina pg-2 como novo primário consumindo o WAL presente em pg-a.