Como usamos a replicação adiada para recuperação de desastres com o PostgreSQL


A replicação não é um backup. Ou não? Veja como usamos a replicação adiada para recuperação, excluindo acidentalmente os atalhos.


Os especialistas em infraestrutura do GitLab são responsáveis ​​pela execução do GitLab.com , a maior instância do GitLab por natureza. Existem 3 milhões de usuários e quase 7 milhões de projetos, e este é um dos maiores sites SaaS de código aberto com uma arquitetura dedicada. Sem o sistema de banco de dados PostgreSQL, a infraestrutura do GitLab.com não irá muito longe, e nós não fazemos isso apenas para tolerância a falhas em caso de falhas nas quais os dados podem ser perdidos. É improvável que tal catástrofe aconteça, mas estamos bem preparados e abastecidos com diferentes mecanismos de backup e replicação.


A replicação não é uma ferramenta de backup de banco de dados para você ( veja abaixo ). Mas agora veremos como recuperar rapidamente dados excluídos acidentalmente usando replicação atrasada: no GitLab.com, o usuário excluiu o atalho para o projeto gitlab-ce e perdeu o contato com solicitações e tarefas de mesclagem.


Com uma réplica atrasada, recuperamos os dados em apenas 1,5 horas. Veja como foi.


Recuperação point-in-time com PostgreSQL


O PostgreSQL possui uma função interna que restaura o estado do banco de dados em um momento específico. Ele se chama Recuperação Point-in-Time (PITR) e usa os mesmos mecanismos que mantêm a relevância da réplica: começando com um instantâneo confiável de todo o cluster de banco de dados (backup básico), aplicamos várias alterações de estado até um determinado momento.


Para usar esta função para um backup a frio, fazemos regularmente um backup básico do banco de dados e o armazenamos no arquivo morto (os arquivos do GitLab ficam no armazenamento em nuvem do Google ). Também monitoramos as alterações no estado do banco de dados arquivando o log de write-ahead log (WAL). E com tudo isso, podemos executar o PITR para recuperação de desastres: começamos com a foto tirada antes do erro e aplicamos as alterações do arquivo WAL até a falha.


O que é replicação adiada?


A replicação adiada é a aplicação de alterações atrasadas no WAL. Ou seja, a transação ocorreu na hora X , mas aparecerá na réplica com um atraso de d na hora X + d .


O PostgreSQL possui 2 maneiras de configurar a réplica física do banco de dados: restaurar a partir do arquivo morto e replicação de streaming. A restauração do arquivo , na verdade, funciona como PITR, mas continuamente: extraímos constantemente as alterações do arquivo WAL e as aplicamos à réplica. E a replicação de streaming recupera diretamente o fluxo WAL do host do banco de dados upstream. Preferimos a recuperação do arquivo morto - é mais fácil de gerenciar e tem desempenho normal, o que não fica atrás do cluster de trabalho.


Como configurar a recuperação adiada do arquivo morto


As opções de recuperação são descritas no arquivo recovery.conf . Um exemplo:


 standby_mode = 'on' restore_command = '/usr/bin/envdir /etc/wal-ed/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"' recovery_min_apply_delay = '8h' recovery_target_timeline = 'latest' 

Com esses parâmetros, configuramos uma réplica lenta com recuperação do arquivo morto. Aqui, o wal-e é usado para extrair segmentos WAL ( restore_command ) do arquivo restore_command , e as alterações serão aplicadas após oito horas ( recovery_min_apply_delay ). A réplica monitorará as alterações na linha do tempo no arquivo morto, por exemplo, devido ao failover no cluster ( recovery_target_timeline ).


Com recovery_min_apply_delay você pode configurar a replicação de streaming atrasada, mas existem alguns truques associados aos slots de replicação, aos comentários de hot spare e assim por diante. O arquivo WAL permite evitá-los.


O parâmetro recovery_min_apply_delay apareceu apenas no PostgreSQL 9.3. Nas versões anteriores, para replicação adiada, você precisa configurar uma combinação de funções de gerenciamento de recuperação ( pg_xlog_replay_pause(), pg_xlog_replay_resume() ) ou manter os segmentos WAL no arquivo pg_xlog_replay_pause(), pg_xlog_replay_resume() por um atraso de tempo.


Como o PostgreSQL faz isso?


Curioso para ver como o PostgreSQL implementa a recuperação adiada. Vejamos recoveryApplyDelay(XlogReaderState) . É chamado a partir do loop principal para cada entrada no WAL.


 static bool recoveryApplyDelay(XLogReaderState *record) { uint8 xact_info; TimestampTz xtime; long secs; int microsecs; /* nothing to do if no delay configured */ if (recovery_min_apply_delay <= 0) return false; /* no delay is applied on a database not yet consistent */ if (!reachedConsistency) return false; /* * Is it a COMMIT record? * * We deliberately choose not to delay aborts since they have no effect on * MVCC. We already allow replay of records that don't have a timestamp, * so there is already opportunity for issues caused by early conflicts on * standbys. */ if (XLogRecGetRmid(record) != RM_XACT_ID) return false; xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK; if (xact_info != XLOG_XACT_COMMIT && xact_info != XLOG_XACT_COMMIT_PREPARED) return false; if (!getRecordTimestamp(record, &xtime)) return false; recoveryDelayUntilTime = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay); /* * Exit without arming the latch if it's already past time to apply this * record */ TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime, &secs, &microsecs); if (secs <= 0 && microsecs <= 0) return false; while (true) { // Shortened: // Use WaitLatch until we reached recoveryDelayUntilTime // and then break; } return true; } 

O ponto principal é que o atraso é baseado no tempo físico registrado na transação timestamp de confirmação ( xtime ). Como você pode ver, o atraso se aplica apenas a confirmações e não toca em outros registros - todas as alterações são aplicadas diretamente e a confirmação é atrasada, para que as alterações sejam exibidas somente após a configuração do atraso.


Como usar réplica lenta para recuperar dados


Digamos que temos um cluster de banco de dados em produção e uma réplica com um atraso de oito horas. Vamos ver como recuperar dados usando o exemplo de exclusão acidental de atalhos .


Quando descobrimos o problema, pausamos a recuperação do arquivo para a réplica lenta:


 SELECT pg_xlog_replay_pause(); 

Com uma pausa, não tivemos o risco de a réplica repetir a solicitação DELETE . Útil se você precisar de tempo para descobrir.


A linha inferior é que a réplica adiada deve atingir o ponto antes da solicitação DELETE . Sabíamos aproximadamente o tempo físico da remoção. Removemos recovery_min_apply_delay e adicionamos recovery_target_time ao recovery.conf . Portanto, a réplica chega ao momento certo sem demora:


 recovery_target_time = '2018-10-12 09:25:00+00' 

Com carimbos de data e hora, é melhor reduzir o excesso para não perder. É verdade que quanto maior a diminuição, mais dados perdemos. Novamente, se passarmos pela solicitação DELETE , tudo será excluído novamente e você terá que iniciar tudo de novo (ou até fazer um backup a frio para o PITR).


Reiniciámos a instância adiada do Postgres e os segmentos WAL foram repetidos até o tempo especificado. Você pode acompanhar o progresso nesta fase, mediante solicitação:


 SELECT -- current location in WAL pg_last_xlog_replay_location(), -- current transaction timestamp (state of the replica) pg_last_xact_replay_timestamp(), -- current physical time now(), -- the amount of time still to be applied until recovery_target_time has been reached '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay; 

Se o carimbo de data e hora não mudar mais, a recuperação estará concluída. Você pode configurar a ação recovery_target_action para fechar, avançar ou pausar uma instância após uma reprodução (por padrão, ela pausa).


O banco de dados chegou a um estado antes dessa solicitação incorreta. Agora você pode, por exemplo, exportar dados. Exportamos os dados excluídos sobre o atalho e todas as conexões com tarefas e solicitações de mesclagem e os transferimos para o banco de dados de trabalho. Se as perdas forem em larga escala, você pode simplesmente promover a réplica e usá-la como principal. Mas então todas as mudanças serão perdidas após o momento em que nos recuperamos.


Em vez de carimbos de data e hora, é melhor usar IDs de transação. É útil escrever esses IDs, por exemplo, para instruções DDL (como DROP TABLE ), usando log_statements = 'ddl' . Se tivéssemos um ID de transação, pegaríamos recovery_target_xid e executaríamos tudo na transação antes da solicitação DELETE .


Voltar ao trabalho é muito simples: remova todas as alterações do recovery.conf e reinicie o Postgres. Em breve, um atraso de oito horas aparecerá na réplica novamente e estamos prontos para futuros problemas.


Benefícios de recuperação


Com uma réplica atrasada, em vez de um backup frio, você não precisa restaurar a imagem inteira do arquivo morto por horas. Por exemplo, precisamos de cinco horas para obter todo o backup básico de 2 TB. E ainda é necessário aplicar todo o WAL diário para recuperar o estado desejado (no pior caso).


Uma réplica atrasada é melhor que um backup frio de duas maneiras:


  1. Não há necessidade de obter todo o backup básico do arquivo morto.
  2. Há uma janela fixa de oito horas de segmentos WAL que precisa ser repetida.

Também checamos constantemente se é possível fazer PITR a partir do WAL e notamos rapidamente danos ou outros problemas no arquivo WAL, monitorando o atraso da réplica atrasada.


Neste exemplo, levamos 50 minutos para recuperar, ou seja, a velocidade era de 110 GB de dados WAL por hora (o arquivo ainda estava no AWS S3 ). No total, resolvemos o problema e restauramos os dados em 1,5 horas.


Conclusão: onde a réplica atrasada é útil (e onde não)


Use a replicação lenta como primeiros socorros se você perder dados acidentalmente e perceber esse desastre dentro do atraso configurado.


Mas lembre-se: a replicação não é um backup.

O backup e a replicação têm objetivos diferentes. Um backup a frio é útil se você acidentalmente fez uma DELETE ou DROP TABLE . Fazemos um backup do armazenamento frio e restauramos o estado anterior da tabela ou de todo o banco de dados. Mas, ao mesmo tempo, a consulta DROP TABLE é reproduzida quase instantaneamente em todas as réplicas no cluster de trabalho, portanto, a replicação regular não o salvará aqui. A replicação em si mantém o banco de dados acessível quando servidores separados são concedidos e distribui a carga.


Mesmo com uma réplica atrasada, às vezes precisamos realmente de um backup a frio em um local seguro se um data center travar, danos ocultos ou outros eventos que você não notará imediatamente. Não há sentido em uma replicação.


Nota No GitLab.com, agora protegemos contra a perda de dados apenas no nível do sistema e não restauramos dados no nível do usuário.

Source: https://habr.com/ru/post/pt445446/


All Articles