PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como atualizar o PostgreSQL 11 para o PostgreSQL 12 com tempo de inatividade zero

A maneira mais intuitiva de atualização de banco de dados que você pode imaginar é gerar uma réplica em uma nova versão e executar um failover do aplicativo nela, e na verdade ele funciona perfeitamente em outros mecanismos. Com o PostgreSQL, isso costumava ser impossível de forma nativa. Para realizar atualizações, você precisava pensar em outras maneiras de atualizar, como usar pg_upgrade, despejar e restaurar, ou usar algumas ferramentas de terceiros como Slony ou Bucardo, todas elas com suas próprias ressalvas. Isso se deve ao modo como o PostgreSQL costumava implementar a replicação.

A replicação de streaming PostgreSQL (a replicação comum do PostgreSQL) é uma replicação física que replica as alterações em nível byte a byte, criando uma cópia idêntica do banco de dados em outro servidor. Esse método tem muitas limitações quando se pensa em um upgrade, pois você simplesmente não pode criar uma réplica em uma versão de servidor diferente ou mesmo em uma arquitetura diferente.

Desde o PostgreSQL 10, ele implementou a replicação lógica embutida que, em contraste com a replicação física, você pode replicar entre as diferentes versões principais do PostgreSQL. Isso, é claro, abre uma nova porta para estratégias de atualização.

Neste blog, veremos como você pode atualizar seu PostgreSQL 11 para PostgreSQL 12 com zero tempo de inatividade usando replicação lógica.

Replicação lógica do PostgreSQL

Replicação lógica é um método de replicação de objetos de dados e suas alterações, com base em sua identidade de replicação (geralmente uma chave primária). É baseado em um modo de publicação e assinatura, onde um ou mais assinantes assinam uma ou mais publicações em um nó publicador.

Uma publicação é um conjunto de alterações geradas a partir de uma tabela ou de um grupo de tabelas (também conhecido como conjunto de replicação). O nó onde uma publicação é definida é referido como publicador. Uma assinatura é o lado downstream da replicação lógica. O nó onde uma assinatura é definida é chamado de assinante e define a conexão com outro banco de dados e conjunto de publicações (uma ou mais) que deseja assinar. Os assinantes extraem dados das publicações que assinam.

A replicação lógica é construída com uma arquitetura semelhante à replicação de streaming físico. É implementado pelos processos "walsender" e "apply". O processo walsender inicia a decodificação lógica do WAL e carrega o plug-in de decodificação lógica padrão. O plugin transforma as alterações lidas do WAL para o protocolo de replicação lógica e filtra os dados de acordo com a especificação da publicação. Os dados são então transferidos continuamente usando o protocolo de replicação de streaming para o operador de aplicação, que mapeia os dados para tabelas locais e aplica as alterações individuais à medida que são recebidas, em uma ordem transacional correta.

A replicação lógica começa tirando um instantâneo dos dados no banco de dados do editor e copiando isso para o assinante. Os dados iniciais nas tabelas assinadas existentes são instantâneos e copiados em uma instância paralela de um tipo especial de processo de aplicação. Esse processo criará seu próprio slot de replicação temporário e copiará os dados existentes. Depois que os dados existentes são copiados, o trabalhador entra no modo de sincronização, o que garante que a tabela seja colocada em um estado sincronizado com o processo de aplicação principal, transmitindo todas as alterações ocorridas durante a cópia de dados inicial usando a replicação lógica padrão. Uma vez que a sincronização é feita, o controle da replicação da tabela é devolvido ao processo de aplicação principal onde a replicação continua normalmente. As alterações no editor são enviadas ao assinante à medida que ocorrem em tempo real.

Como atualizar PostgreSQL 11 para PostgreSQL 12 usando replicação lógica

Vamos configurar a replicação lógica entre duas versões principais diferentes do PostgreSQL (11 e 12) e, claro, depois que você tiver isso funcionando, é apenas uma questão de executar um failover de aplicativo no banco de dados com a versão mais recente.

Vamos realizar as seguintes etapas para colocar a replicação lógica em funcionamento:

  • Configure o nó do editor
  • Configure o nó do assinante
  • Criar o usuário assinante
  • Criar uma publicação
  • Crie a estrutura da tabela no assinante
  • Crie a assinatura
  • Verifique o status da replicação

Então vamos começar.

No lado do publicador, vamos configurar os seguintes parâmetros no arquivo postgresql.conf:

  • listen_addresses: Em qual(is) endereço(s) IP escutar. Usaremos '*' para todos.
  • wal_level: Determina quanta informação é gravada no WAL. Vamos defini-lo como "lógico".
  • max_replication_slots :especifica o número máximo de slots de replicação que o servidor pode suportar. Ele deve ser definido para pelo menos o número de assinaturas esperadas para conexão, além de alguma reserva para sincronização de tabela.
  • max_wal_senders: Especifica o número máximo de conexões simultâneas de servidores em espera ou clientes de backup de base de streaming. Ele deve ser definido como pelo menos o mesmo que max_replication_slots mais o número de réplicas físicas conectadas ao mesmo tempo.

Lembre-se de que alguns desses parâmetros requerem uma reinicialização do serviço PostgreSQL para serem aplicados.

O arquivo pg_hba.conf também precisa ser ajustado para permitir a replicação. Você precisa permitir que o usuário de replicação se conecte ao banco de dados.

Com base nisso, vamos configurar o publicador (neste caso o servidor PostgreSQL 11) da seguinte forma:

postgresql.conf:

listen_addresses = '*'

wal_level = logical

max_wal_senders = 8

max_replication_slots = 4

​pg_hba.conf:

# TYPE  DATABASE        USER            ADDRESS                 METHOD

host     all     rep1     10.10.10.131/32     md5


Você deve alterar o usuário (neste exemplo rep1), que será usado para replicação, e o endereço IP 10.10.10.131/32 para o IP que corresponde ao seu nó PostgreSQL 12.

No lado do assinante, também requer que os max_replication_slots sejam definidos. Nesse caso, deve ser definido pelo menos o número de assinaturas que serão adicionadas ao assinante.

Os outros parâmetros que também precisam ser definidos aqui são:

  • max_logical_replication_workers :especifica o número máximo de trabalhadores de replicação lógica. Isso inclui os trabalhadores de aplicação e de sincronização de tabela. Os trabalhadores de replicação lógica são obtidos do pool definido por max_worker_processes. Ele deve ser definido para pelo menos o número de assinaturas, novamente mais alguma reserva para a sincronização da tabela.
  • max_worker_processes :define o número máximo de processos em segundo plano que o sistema pode suportar. Ele pode precisar ser ajustado para acomodar trabalhadores de replicação, pelo menos max_logical_replication_workers + 1. Este parâmetro requer uma reinicialização do PostgreSQL.

Então, você deve configurar o assinante (neste caso o servidor PostgreSQL 12) da seguinte forma:

postgresql.conf:

listen_addresses = '*'

max_replication_slots = 4

max_logical_replication_workers = 4

max_worker_processes = 8

Como este PostgreSQL 12 será o novo nó primário em breve, você deve considerar adicionar os parâmetros wal_level e archive_mode nesta etapa, para evitar uma nova reinicialização do serviço posteriormente.

wal_level = logical

archive_mode = on

Esses parâmetros serão úteis se você quiser adicionar uma nova réplica ou usar backups PITR.

No editor, você deve criar o usuário com o qual o assinante se conectará:

world=# CREATE ROLE rep1 WITH LOGIN PASSWORD '*****' REPLICATION;

CREATE ROLE

A função usada para a conexão de replicação deve ter o atributo REPLICATION. O acesso para a função deve ser configurado em pg_hba.conf e deve ter o atributo LOGIN.

Para poder copiar os dados iniciais, a função usada para a conexão de replicação deve ter o privilégio SELECT em uma tabela publicada.

world=# GRANT SELECT ON ALL TABLES IN SCHEMA public to rep1;

GRANT

Criaremos a publicação pub1 no nó do editor, para todas as tabelas:

world=# CREATE PUBLICATION pub1 FOR ALL TABLES;

CREATE PUBLICATION

O usuário que irá criar uma publicação deve ter o privilégio CREATE no banco de dados, mas para criar uma publicação que publique todas as tabelas automaticamente, o usuário deve ser um superusuário.

Para confirmar a publicação criada vamos utilizar o catálogo pg_publication. Este catálogo contém informações sobre todas as publicações criadas no banco de dados.

world=# SELECT * FROM pg_publication;

-[ RECORD 1 ]+-----

pubname      | pub1

pubowner     | 10

puballtables | t

pubinsert    | t

pubupdate    | t

pubdelete    | t

pubtruncate  | t

Descrições das colunas:

  • nome do pub :Nome da publicação.
  • proprietário do pub :Proprietário da publicação.
  • pubaltables :se verdadeiro, esta publicação inclui automaticamente todas as tabelas do banco de dados, incluindo as que serão criadas no futuro.
  • pubinserir :se true, as operações INSERT são replicadas para tabelas na publicação.
  • pubupdate :se true, as operações UPDATE são replicadas para tabelas na publicação.
  • pubdelete :se true, as operações DELETE são replicadas para tabelas na publicação.
  • pubtruncate :se true, as operações TRUNCATE são replicadas para tabelas na publicação.

Como o esquema não é replicado, você deve fazer um backup no PostgreSQL 11 e restaurá-lo no seu PostgreSQL 12. O backup será feito apenas para o esquema, pois as informações serão replicadas no transferir.

No PostgreSQL 11:

$ pg_dumpall -s > schema.sql

No PostgreSQL 12:

$ psql -d postgres -f schema.sql

Depois de ter seu esquema no PostgreSQL 12, você precisa criar a assinatura, substituindo os valores de host, dbname, user e password pelos que correspondem ao seu ambiente.

PostgreSQL 12:

world=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=10.10.10.130 dbname=world user=rep1 password=*****' PUBLICATION pub1;

NOTICE:  created replication slot "sub1" on publisher

CREATE SUBSCRIPTION

O acima iniciará o processo de replicação, que sincroniza o conteúdo inicial das tabelas na publicação e, em seguida, inicia a replicação de alterações incrementais nessas tabelas.

O usuário que cria uma assinatura deve ser um superusuário. O processo de aplicação de assinatura será executado no banco de dados local com os privilégios de um superusuário.

Para verificar a assinatura criada, você pode usar o catálogo pg_stat_subscription. Essa exibição conterá uma linha por assinatura para o trabalhador principal (com PID nulo se o trabalhador não estiver em execução) e linhas adicionais para trabalhadores que manipulam a cópia de dados inicial das tabelas inscritas.

world=# SELECT * FROM pg_stat_subscription;

-[ RECORD 1 ]---------+------------------------------

subid                 | 16422

subname               | sub1

pid                   | 476

relid                 |

received_lsn          | 0/1771668

last_msg_send_time    | 2020-09-29 17:40:34.711411+00

last_msg_receipt_time | 2020-09-29 17:40:34.711533+00

latest_end_lsn        | 0/1771668

latest_end_time       | 2020-09-29 17:40:34.711411+00

Descrições das colunas:

  • subid :OID da assinatura.
  • subnome :nome da assinatura.
  • pid :ID do processo do trabalhador de assinatura.
  • relid :OID da relação que o trabalhador está sincronizando; null para o principal trabalhador de aplicação.
  • recebido_lsn :último local de registro write-ahead recebido, sendo o valor inicial deste campo 0.
  • last_msg_send_time :hora de envio da última mensagem recebida do remetente WAL de origem.
  • last_msg_receipt_time :hora de recebimento da última mensagem recebida do remetente WAL de origem.
  • latest_end_lsn :último local de registro de gravação antecipada relatado ao remetente WAL de origem.
  • latest_end_time :hora do último local de registro de gravação antecipada relatado ao remetente WAL de origem.

Para verificar o status da replicação no nó primário, você pode usar pg_stat_replication:

world=# SELECT * FROM pg_stat_replication;

-[ RECORD 1 ]----+------------------------------

pid              | 527

usesysid         | 16428

usename          | rep1

application_name | sub1

client_addr      | 10.10.10.131

client_hostname  |

client_port      | 35570

backend_start    | 2020-09-29 17:40:04.404905+00

backend_xmin     |

state            | streaming

sent_lsn         | 0/1771668

write_lsn        | 0/1771668

flush_lsn        | 0/1771668

replay_lsn       | 0/1771668

write_lag        |

flush_lag        |

replay_lag       |

sync_priority    | 0

sync_state       | async

Descrições das colunas:

  • pid :ID do processo de um processo do remetente WAL.
  • usesysid :OID do usuário conectado a este processo de remetente WAL.
  • usename :nome do usuário conectado a este processo de remetente WAL.
  • application_name :nome do aplicativo que está conectado a este remetente WAL.
  • client_addr :endereço IP do cliente conectado a este remetente WAL. Se este campo for nulo, indica que o cliente está conectado através de um soquete Unix na máquina do servidor.
  • client_hostname :Nome do host do cliente conectado, conforme relatado por uma pesquisa de DNS reversa de client_addr. Este campo só será não nulo para conexões IP e somente quando log_hostname estiver ativado.
  • port_cliente :número da porta TCP que o cliente está usando para comunicação com este remetente WAL, ou -1 se um soquete Unix for usado.
  • backend_start :hora em que este processo foi iniciado.
  • backend_xmin :horizonte xmin deste standby relatado por hot_standby_feedback.
  • estado :Estado atual do remetente WAL. Os valores possíveis são:startup, catchup, streaming, backup e stop.
  • sent_lsn :último local de registro de gravação antecipada enviado nesta conexão.
  • write_lsn :último local de registro de gravação antecipada gravado em disco por este servidor em espera.
  • flush_lsn :último local de log de gravação antecipada liberado para o disco por este servidor em espera.
  • replay_lsn :último local de log de gravação antecipada reproduzido no banco de dados neste servidor em espera.
  • write_lag :tempo decorrido entre a liberação do WAL recente localmente e o recebimento da notificação de que este servidor em espera o gravou (mas ainda não o liberou ou aplicou).
  • flush_lag :Tempo decorrido entre a liberação do WAL recente localmente e o recebimento da notificação de que este servidor em espera o gravou e liberou (mas ainda não o aplicou).
  • replay_lag :tempo decorrido entre a liberação do WAL recente localmente e o recebimento da notificação de que este servidor em espera o gravou, liberou e aplicou.
  • sync_priority :Prioridade deste servidor em espera para ser escolhido como espera síncrona em uma replicação síncrona baseada em prioridade.
  • sync_state :estado síncrono deste servidor em espera. Os valores possíveis são assíncrono, potencial, sincronização, quorum.

Para verificar quando a transferência inicial foi concluída, você pode verificar o log do PostgreSQL no assinante:

2020-09-29 17:40:04.403 UTC [476] LOG:  logical replication apply worker for subscription "sub1" has started

2020-09-29 17:40:04.411 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has started

2020-09-29 17:40:04.422 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has started

2020-09-29 17:40:04.516 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has finished

2020-09-29 17:40:04.522 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has started

2020-09-29 17:40:04.570 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has finished

2020-09-29 17:40:04.676 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has finished

Ou verificando a variável srsubstate no catálogo pg_subscription_rel. Este catálogo contém o estado de cada relação replicada em cada assinatura.

world=# SELECT * FROM pg_subscription_rel;

 srsubid | srrelid | srsubstate | srsublsn

---------+---------+------------+-----------

   16422 |   16386 | r          | 0/1771630

   16422 |   16392 | r          | 0/1771630

   16422 |   16399 | r          | 0/1771668

(3 rows)

Descrições das colunas:

  • srsubid :referência à assinatura.
  • srrelid :referência à relação.
  • srsubstate :Código de estado:i =inicializar, d =dados estão sendo copiados, s =sincronizado, r =pronto (replicação normal).
  • srsublsn :Fim do LSN para os estados s e r.

Você pode inserir alguns registros de teste em seu PostgreSQL 11 e validar que você os possui em seu PostgreSQL 12:

PostgreSQL 11:

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5001,'city1','USA','District1',10000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5002,'city2','ITA','District2',20000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5003,'city3','CHN','District3',30000);

INSERT 0 1

PostgreSQL 12:

world=# SELECT * FROM city WHERE id>5000;

  id  | name  | countrycode | district  | population

------+-------+-------------+-----------+------------

 5001 | city1 | USA         | District1 |      10000

 5002 | city2 | ITA         | District2 |      20000

 5003 | city3 | CHN         | District3 |      30000

(3 rows)

Neste ponto, você tem tudo pronto para apontar sua aplicação para o PostgreSQL 12.

Para isso, antes de tudo, você precisa confirmar que não tem atraso de replicação.

No nó primário:

world=# SELECT  application_name,  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) lag FROM pg_stat_replication;

-[ RECORD 1 ]----+-----

application_name | sub1

lag              | 0

E agora, você só precisa alterar seu endpoint de seu aplicativo ou balanceador de carga (se tiver um) para o novo servidor PostgreSQL 12.

Se você possui um load balancer como o HAProxy, você pode configurá-lo usando o PostgreSQL 11 como ativo e o PostgreSQL 12 como backup, desta forma:

Então, se você acabou de desligar o antigo nó primário no PostgreSQL 11, o servidor de backup, neste caso no PostgreSQL 12, passa a receber o tráfego de forma transparente para o usuário/aplicativo.

No final da migração, você pode excluir a assinatura em seu novo nó primário no PostgreSQL 12:

world=# DROP SUBSCRIPTION sub1;

NOTICE:  dropped replication slot "sub1" on publisher

DROP SUBSCRIPTION

E verifique se foi removido corretamente:

world=# SELECT * FROM pg_subscription_rel;

(0 rows)

world=# SELECT * FROM pg_stat_subscription;

(0 rows)

Limitações

Antes de usar a replicação lógica, lembre-se das seguintes limitações:

  • O esquema do banco de dados e os comandos DDL não são replicados. O esquema inicial pode ser copiado usando pg_dump --schema-only.
  • Os dados de sequência não são replicados. Os dados em colunas seriais ou de identidade apoiados por sequências serão replicados como parte da tabela, mas a própria sequência ainda mostrará o valor inicial no assinante.
  • A replicação de comandos TRUNCATE é suportada, mas alguns cuidados devem ser tomados ao truncar grupos de tabelas conectadas por chaves estrangeiras. Ao replicar uma ação de truncar, o assinante truncará o mesmo grupo de tabelas que foi truncado no publicador, especificado explicitamente ou coletado implicitamente via CASCADE, menos as tabelas que não fazem parte da assinatura. Isso funcionará corretamente se todas as tabelas afetadas fizerem parte da mesma assinatura. Mas se algumas tabelas a serem truncadas no assinante tiverem links de chave estrangeira para tabelas que não fazem parte da mesma (ou de nenhuma) assinatura, a aplicação da ação truncar no assinante falhará.
  • Objetos grandes não são replicados. Não há solução para isso, além de armazenar dados em tabelas normais.
  • A replicação só é possível de tabelas base para tabelas base. Ou seja, as tabelas na publicação e no lado da assinatura devem ser tabelas normais, não exibições, exibições materializadas, tabelas raiz de partição ou tabelas estrangeiras. No caso de partições, você pode replicar uma hierarquia de partição individualmente, mas atualmente não pode replicar para uma configuração particionada de forma diferente.

Conclusão

Manter seu servidor PostgreSQL atualizado realizando atualizações regulares foi uma tarefa necessária, mas difícil até a versão 10 do PostgreSQL. Felizmente agora é uma história diferente graças à replicação lógica.

Neste blog, fizemos uma breve introdução à replicação lógica, um recurso do PostgreSQL introduzido nativamente na versão 10, e mostramos como ele pode ajudá-lo a realizar essa atualização do desafio do PostgreSQL 11 para o PostgreSQL 12 com uma estratégia de tempo de inatividade zero.