postgres postgresql replication

Replicação física

O WAL é uma longa sequência de alterações físicas dos blocos de disco registradas em uma linha do tempo que pode ser armazenada, comunicada e reproduzida. E um backup é uma cópia física dos dados em um ponto da linha do tempo, que pode ser restaurado e avançado adiante ao longo daquela linha do tempo e de outras que tenham surgido dela, contanto que o WAL esteja íntegro desde o momento do backup até o alvo desejado.

Uma replicação física é um backup em ciclo de contínua restauração de WAL, como se o alvo da restauração estivesse sempre adiante. O PostgreSQL primário aceita conexões que atendem transações de leitura e escrita. O PostgreSQL réplica (também chamado de secundário ou standby) também aceita conexões que atendem transações, mas estas apenas de leitura. Isso porque qualquer escrita feita pela réplica localmente ou seria sobrescrita pelos blocos provenientes da replicação, gerando inconsistências nos dados, ou quebraria a cadeia de replicação.

Atenção: O PostgreSQL não usa termos como mestre, master, escravo ou slave pelo forte significado social negativo que essas palavras carregam. Os termos corretos são primário/primary e secundário/réplica/standby.

A seguir temos uma descrição simplificada da replicação de um primário (à esquerda) para a réplica (à direita), com backups sendo criados (A) e restaurados (B) e arquivamento de WAL em um terceiro local, abaixo. O diagrama pode ser lido seguindo os passos:

  1. Aplicação faz leituras e escritas nos bancos de dados do primário.
  2. Alterações nos bancos de dados mudam blocos em disco.
  3. Mudanças dos blocos são escritas no WAL (a) e nas conexões de replicação (b).
  4. Segmentos de WAL completados são arquivados (c).
  5. Segmentos de WAL são buscados do arquivamento pela réplica.
  6. Mudanças de blocos são trazidas pela conexão de replicação e pelos segmentos de WAL.
  7. Blocos são escritos em disco na réplica.
  8. Aplicação consulta os bancos de dados da réplica como somente leitura.
                                   ╔═══════════╗↔──────────────────┬─────┐
                                   ║ Aplicação ║╗↔─────────────────┼─────┤
                                   ╚═══════════╝║╗↔────────────────┼─────┤
                                    ╚═══════════╝║↔────────────────┼─────┤
                                     ╚═══════════╝↔────────────────┼─────┤
                                                                (1)│     │(8)
                   ┌─────────────────────┐                         │     │                      ┌────────────────────┐
╔══════════════════╡PostgreSQL (primário)╞═════════════════════╗   │     │   ╔══════════════════╡PostgreSQL (réplica)╞══════════════════════╗
║                  └─────────────────────┘                     ║   │     │   ║                  └────────────────────┘                      ║
╠═╡global/╞═══════╗                     ╔══════════════╡base/╞═╣   │     │   ╠═╡base/╞══════════════╗                     ╔═══════╡global/╞═╣
║                 ║                     ║                      ║   │     │   ║                      ║                     ║                 ║
║     O     O     ║                     ║ ┏━┥banco de dados┝━┓ ║   │     │   ║ ┏━┥banco de dados┝━┓ ║                     ║     O     O     ║
║    ─┼─   ─┼─    ║                 (2) ║ ┃ ┌─┤schema├─────┐ ┃ ║   │     │   ║ ┃ ┌─┤schema├─────┐ ┃ ║ (7)                 ║    ─┼─   ─┼─    ║
║     │     │     ║                  ┊←┈╢ ┃ │ ☑ tabelas    │←╂─╫───┤     ├───╫─╂→│ ☑ tabelas    │ ┃ ╟←┈┊                  ║     │     │     ║
║    ╱ ╲   ╱ ╲    ║                  ┊←┈╢ ┃ │ ☑ índices    │←╂─╫───┤     ├───╫─╂→│ ☑ índices    │ ┃ ╟←┈┊                  ║    ╱ ╲   ╱ ╲    ║
║ ☑ usuários      ║                  ┊←┈╢ ┃ │ ☑ visões     │←╂─╫───┤     ├───╫─╂→│ ☑ visões     │ ┃ ╟←┈┊                  ║ ☑ usuários      ║
║ ☑ grupos        ║                  ┊←┈╢ ┃ │ ☑ sequências │←╂─╫───┤     ├───╫─╂→│ ☑ sequências │ ┃ ╟←┈┊                  ║ ☑ grupos        ║
║ ☑ slots de      ║                  ┊←┈╢ ┃ │ ☑ …          │←╂─╫───┘     └───╫─╂→│ ☑ …          │ ┃ ╟←┈┊                  ║ ☑ slots de      ║
║ ☑ replicação    ║                  ┊  ║ ┃ └──────────────┘ ┃ ║             ║ ┃ └──────────────┘ ┃ ║  ┊                  ║ ☑ replicação    ║
║ ☑ pg_control┅┅┅┅╫┅┅┓               ┊  ║ ┗━━━━━━━━━━━━━━━━━━┛ ║             ║ ┗━━━━━━━━━━━━━━━━━━┛ ║  ┊               ┏┅┅╫┅☑ pg_control    ║
║                 ║  ┋               ┊  ║                      ║             ║                      ║  ┊               ┋  ║                 ║
╠═════════════════╝  ┋               ┊  ╚══════════════════════╣             ╠══════════════════════╝  ┊               ┋  ╚═════════════════╣
║                    ┋            (3)↓                         ║             ║                         ↑               ┋                    ║
╠═╡pg_wal/╞══════════╪═══════════════╪═══════════╗             ║     (b)     ║             ╔═══════════╪═══════════════╪══════════╡pg_wal/╞═╣
║                    ┋               ┊┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┈┈┈→╫→┈┈┈┈┈┈┈┈┈→┊(6)            ┋                    ║
║  ┌────────────┐ ┌──↓─────────┐  (a)↓           ║             ║             ║             ║           ↑     ┌─────────↓──┐ ┌────────────┐  ║
║ →│+--++--+--++│→│-++++-++---+│→╷++-.........╷→ ║     (c)     ║             ║     (5)     ║ ←╷.........-++╷←│+---++-++++-│←│++--+--++--+│← ║
║  └┤/…00000012├┘ └┤/…00000013├┘↓└┤/…00000014├┘  ║  ┌───────┐  ║             ║  ┌───────┐  ║  └┤/…00000014├┘↑└┤/…00000013├┘ └┤/…00000012├┘  ║
║                            (4)└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈→╫→┈│…++---+│┈→╫→┈┈┐     ┌┈┈→╫→┈│…++---+│┈→╫→┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘                               ║
║                                                ║  └┤/…013├┘  ║   ┊     ┊   ║  └┤/…013├┘  ║                                                ║
╚════════════════════════════════════════════════╩═════════════╝   ┊     ┊   ╚═════════════╩════════════════════════════════════════════════╝
                           ╏                                       ┊     ┊                                       ╏
                       (A) ╏                                       ┊     ┊                                       ╏ (B)
                           ╏                                       ┊     ┊                                       ╏
                           ╏                                       ↓     ↑                                       ╏
                           ╏       ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓       ╏
                           ╏       ┃     ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐     ┃       ╏
                       ╔═══════╗   ┃    →│…++-+++│→│…+++---│→│…----++│→│…+--+-+│→│…-+--++│→│…++---+│→    ┃   ╔═══════╗
                       ╠══╗ ╔══╣   ┃     └┤/…008├┘ └┤/…009├┘ └┤/…010├┘ └┤/…011├┘ └┤/…012├┘ └┤/…013├┘     ┃   ╠══╗ ╔══╣
                       ╠══╝ ╚══╣   ┠────────↑────────↑────────────↑─────────↑─────────↑───────↑──────────┨   ╠══╝ ╚══╣
                       ╠═════╗ ║   ┃      ╔═══════════════╗   ╔═══════════════╗   ╔═══════════════╗      ┃   ║ ╔═════╣
                       ╚═════╩═╝   ┃      ╠═════╗   ╔═════╣   ╠═════╗   ╔═════╣   ╠═════╗   ╔═════╣      ┃   ╚═╩═════╝
                           ╏       ┃      ║     ║   ║     ║   ║     ║   ║     ║   ║     ║   ║     ║      ┃       ╏
                           ┗╍╍╍╍╍╍╸┃………………╠═════╝   ╚═════╣………╠═════╝   ╚═════╣………╠═════╝   ╚═════╣………………┃╺╍╍╍╍╍╍┛
                                   ┃      ╠══════════╗    ║   ╠══════════╗    ║   ╠══════════╗    ║      ┃
                                   ┃      ║          ║    ║   ║          ║    ║   ║          ║    ║      ┃
                                   ┃      ╚══════════╩════╝   ╚══════════╩════╝   ╚══════════╩════╝      ┃
                                   ┃                        ┌──────────────────┐                         ┃
                                   ┗━━━━━━━━━━━━━━━━━━━━━━━━┥Servidor de backup┝━━━━━━━━━━━━━━━━━━━━━━━━━┛
                                                            └──────────────────┘

Para a réplica acompanhar o primário, ela depende do recebimento confiável de todo o WAL gerado. Esse recebimento pode sofrer atrasos e até interrupções causadas por problemas de rede ou indisponibilidade da réplica. Mas para que esses eventos não causem lacunas na linha do tempo de WAL, é vital que exista ao menos uma estratégia que permita que o WAL volte a ser transmitido assim que possível e que evite as lacunas. Duas estratégias existem para tratar esse problema.

A primeira é o arquivamento de segmentos de WAL. Com ela, o primário continua trabalhando e arquivando o trabalho feito. Quando a réplica retorna, ela recupera os segmentos do arquivamento e consegue reaplicar todas as alterações até alcançar o estado mais recente do primário. Ela é adequada para a restauração de backups e indisponibilidades de longo prazo, mas não para replicações em tempo real, já que dependem do segmento ser preenchido e arquivado. Adicionalmente, o primário apenas remove o segmento local quando o arquivamento dele foi feito com sucesso, o que evita riscos de perdas de segmentos mas aumenta o espaço ocupado em pg_wal no primário enquanto a falha persistir.

A segunda estratégia é pela conexão de replicação com um slot de replicação.

Slots de replicação

Um slot de replicação é uma forma do primário conhecer uma réplica e de lembrar o último ponto recebido pela réplica. Com isso, se a réplica fica indisponível temporariamente, o primário não recicla os segmentos que são necessários para ela, garantindo que quando a conexão for reestabelecida, a réplica conseguirá receber toda a linha do tempo sem lacunas.

É possível criar um slot de replicação com a função pg_create_physical_replication_slot():

[[local]:5432] postgres@postgres=# SELECT pg_create_physical_replication_slot('pg-2');

E usar em uma conexão adicionando o parâmetro primary_slot_name nas configurações da réplica:

primary_slot_name = 'pg-2'

Replicação assíncrona e síncrona

Quando uma transação altera tuplas, as novas versões são escritas diretamente (mas não necessariamente imediatamente, em função de buffers de escrita) na tabela. Mas essas tuplas novas só são visíveis para outras transações após um COMMIT de sucesso daquela transação.

Em um primário independente, o comando COMMIT faz com que a escrita dos registros no WAL seja garantida antes do sucesso ser retornado para a aplicação.

Esse também é o comportamento em um primário com replicações assíncronas, que são o tipo padrão de replicação. Ou seja, as transações são processadas completamente no primário para posteriormente serem replicadas para o secundário através do WAL.

Mas em um sistema com replicação síncrona o comportamento em torno da garantia de escrita é diferente. Nesse caso, o primário registra o COMMIT no WAL, replica o WAL para o secundário e aguarda a confirmação de que a transação foi escrita em disco no secundário antes do sucesso ser retornado para a aplicação.

As diferenças do modo assíncrono sobre o síncrono são:

Para configurar um primário com replicação síncrona, adicionamos o nome da conexão de replicação (definido por application_name na conexão iniciada pela réplica) na configuração synchronous_standby_names:

synchronous_standby_names = 'pg-2'

Também é possível atenuar o risco de indisponibilidade usando mais réplicas e aguardando a escrita de apenas algumas delas. Por exemplo, com três réplicas, mas aguardando apenas uma:

synchronous_standby_names = 'ANY 1 (pg-2, pg-3, pg-4)'

E podemos aguardar as primeiras conexões ativas da lista, ou seja, pg-2 e pg-3 na maior parte do tempo, mas incluindo pg-4 nos momentos em que uma das duas primeiras estiver inacessível.

synchronous_standby_names = 'FIRST 2 (pg-2, pg-3, pg-4)'

O termo especial * inclui toda conexão de replicação:

synchronous_standby_names = 'ANY 1 (*)'

Replicação cascateada

Com a replicação é possível distribuir as tarefas e, portanto, a carga do sistema em nós além do primário. Como visto anteriormente, com uma réplica podemos direcionar as transações de leitura e escrita para o primário (1) e transações somente leitura para a réplica (2).

Adicionalmente, a réplica pode ser usada para criação de backups (3), o que alivia o primário dessa tarefa. Ainda assim é preferível que o primário faça o arquivamento, em geral por conexão de replicação com slot (4), para minimizar a possível perda de transações em andamento no caso de falha do primário.

Por fim, cada réplica pode ser configurada, assim como o primário, para aceitar conexões de replicação e, portanto, para replicar o WAL para outras. Isso é chamado replicação cascateada e evita que o primário tenha que enviar grandes volumes de WAL através de diversas conexões de replicação. Ao mesmo tempo, permite que a aplicação perceba uma boa escalabilidade horizontal da leitura se direcionar as elas para as réplicas (5).

Novas réplicas são instanciadas a partir do backup (6).

  ╔═══════════╗↔───────────────┬──────────────────┐
  ║ Aplicação ║╗↔──────────────┼───────────────────┐
  ╚═══════════╝║╗↔─────────────┼────────────────────┐
   ╚═══════════╝║↔─────────────┼─────────────────────┐
    ╚═══════════╝↔─────────────┼──────────────────────┐
    (1) │                  (2) │              (5) │││││
        │                      │                  ↓↓↓↓↓
        │                      │               ┌─────────┐
        │                      │             ╔═╡ Réplica ╞═╗
        │                      │         ┌┈┈→╢ └─────────┘ ║
        │                      │         ┊   ╚═════════════╝
        │                      │         ┊     ┌─────────┐
        │                      │         ┊   ╔═╡ Réplica ╞═╗
        │                      │         ┊┈┈→╢ └─────────┘ ║
        ↓                      ↓         ┊   ╚═════════════╝
  ┌──────────┐           ┌─────────┐     ┊     ┌─────────┐
╔═╡ Primário ╞═╗       ╔═╡ Réplica ╞═╗   ┊   ╔═╡ Réplica ╞═╗
║ └──────────┘ ╟→┈┈┬┈┈→╢ └─────────┘ ╟→┈┈┴┈┈→╢ └─────────┘ ║
╚══════════════╝   ┊   ╚═════════════╝       ╚═════════════╝
                   ┊          ╏                   (6) ╏
                   ┊          ╏                       ╏
               (4) ↓      (3) ╏                       ╏
     ┏━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓    ╏
     ┃ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┃    ╏
     ┃→└┤…├┘→└┤…├┘→└┤…├┘→└┤…├┘→└┤…├┘→└┤…├┘→└┤…├┘→┃    ╏
     ┠──↑─────↑─────↑────↑─────↑──↑──────↑───↑───┨    ╏
     ┃  ╔═══════╗ ╔═══════╗ ╔═══════╗ ╔═══════╗  ┃    ╏
     ┃  ╠══╗ ╔══╣ ╠══╗ ╔══╣ ╠══╗ ╔══╣ ╠══╗ ╔══╣  ┃    ╏
     ┃……╠══╝ ╚══╣…╠══╝ ╚══╣…╠══╝ ╚══╣…╠══╝ ╚══╣……┃╺╍╍╍┛
     ┃  ╠═════╗ ║ ╠═════╗ ║ ╠═════╗ ║ ╠═════╗ ║  ┃
     ┃  ╚═════╩═╝ ╚═════╩═╝ ╚═════╩═╝ ╚═════╩═╝  ┃
     ┃           ┌──────────────────┐            ┃
     ┗━━━━━━━━━━━┥Servidor de backup┝━━━━━━━━━━━━┛
                 └──────────────────┘

Visões

Algumas visões de sistema apresentam o estado da replicação:

Configurações

A partir da versão 12, todas as configurações de replicação são colocadas no postgresql.conf. Em versões anteriores, as configurações de arquivamento também são colocadas no postgresql.conf mas as configurações de restauração são colocadas no recovery.conf, localizado no mesmo diretório.

As configurações de arquivamento foram vistas em WAL.

As configurações de restauração são:

Prática

Crie uma réplica da instância de pg-1 na máquina pg-2 com as seguintes características: