
A replicação não é um backup. Ou é? Veja como usamos a replicação atrasada para recuperação após deletar acidentalmente os atalhos.
no GitLab são responsáveis pela operação — a maior instância do GitLab no mundo. Aqui estão 3 milhões de usuários e quase 7 milhões de projetos, sendo um dos maiores sites de SaaS de código aberto com arquitetura dedicada. Sem o sistema de banco de dados PostgreSQL, a infraestrutura do GitLab.com não iria longe, e fazemos o que for necessário para garantir a resiliência em casos de falha, quando dados podem ser perdidos. É improvável que tal catástrofe aconteça, mas estamos bem preparados e temos diversos mecanismos de backup e replicação.
A replicação não é uma solução de backup de banco de dados (). Mas agora veremos como recuperar rapidamente dados excluídos acidentalmente com a ajuda da replicação atrasada: quando um usuário para o projeto e perdeu a conexão com as solicitações de mesclagem e tarefas.
Com a replicação atrasada, recuperamos os dados em apenas 1,5 horas. Veja como isso aconteceu.
Recuperação em um ponto no tempo com PostgreSQL
O PostgreSQL possui uma função embutida que restaura o estado do banco de dados em um determinado ponto no tempo. Ela é chamada (PITR) e utiliza os mesmos mecanismos que mantêm a replicação atualizada: começando com uma captura confiável de todo o cluster de banco de dados (backup base), aplicamos uma série de alterações até um ponto no tempo específico.
Para usar essa função para um backup frio, fazemos backups base do banco de dados regularmente e o armazenamos em um arquivo (os arquivos do GitLab vivem em ). Além disso, monitoramos as alterações de estado do banco de dados, arquivando o log de gravação antecipada (, WAL). Com tudo isso, podemos realizar a PITR para recuperação de desastres: começamos com a captura feita antes do erro e aplicamos as alterações do arquivo WAL até a falha.
O que é replicação atrasada?
A replicação atrasada é a aplicação de alterações do WAL com um atraso. Ou seja, a transação ocorreu na hora X, mas na replicação ela aparecerá com um atraso d de uma hora X + d.
No PostgreSQL, existem 2 maneiras de configurar a replicação física do banco de dados: recuperação a partir de um arquivo e replicação em streaming. , na verdade, funciona como PITR, mas de forma contínua: estamos constantemente extraindo mudanças do arquivo WAL e aplicando-as na réplica. A extrai diretamente o fluxo WAL do host de banco de dados superior. Preferimos a recuperação a partir do arquivo — é mais fácil de gerenciar e tem um desempenho normal que não fica aquém do cluster de trabalho.
Como configurar a recuperação atrasada a partir do arquivo
estão descritas no arquivo recovery.conf. Exemplo:
standby_mode = 'on'
restore_command = ' /usr/bin/envdir /etc/wal-e.d/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"'
recovery_min_apply_delay = '8h'
recovery_target_timeline = 'latest'Com essas configurações, configuramos a réplica atrasada com recuperação a partir do arquivo. Aqui usamos para extrair os segmentos WAL (restore_command) do arquivo, e as mudanças serão aplicadas após oito horas (recovery_min_apply_delay). A réplica irá monitorar as mudanças na linha do tempo do arquivo, por exemplo, devido a uma falha no cluster (recovery_target_timeline).
Com recovery_min_apply_delay é possível configurar a replicação streaming com atraso, mas há algumas pegadinhas que envolvem slots de replicação, feedback de failover e outros. O arquivo WAL permite evitá-las.
Parâmetro recovery_min_apply_delay apareceu apenas no PostgreSQL 9.3. Em versões anteriores, para replicação atrasada, era necessário configurar uma combinação de (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ou manter os segmentos WAL no arquivo durante o tempo de atraso.
Como o PostgreSQL faz isso?
É interessante ver como o PostgreSQL implementa a recuperação atrasada. Vamos olhar para . Ele é chamado no para cada registro do WAL.
static bool
recoveryApplyDelay(XLogReaderState *record)
{
uint8 xact_info;
TimestampTz xtime;
long secs;
int microsecs;
/* nada para fazer se nenhuma demora estiver configurada */
if (recovery_min_apply_delay <= 0)
return false;
/* nenhuma demora é aplicada em um banco de dados que ainda não está consistente */
if (!reachedConsistency)
return false;
/*
* É um registro de COMMIT?
*
* Escolhemos deliberadamente não atrasar abortos, pois eles não têm efeito sobre
* MVCC. Já permitimos a reprodução de registros que não têm um timestamp,
* então já há oportunidades para problemas causados por conflitos precoces em
* standby.
* /
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);
/*
* Saia sem armar o latch se já estiver passado o tempo para aplicar este
* registro
* /
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
&secs, µsecs);
if (secs <= 0 && microsecs <= 0)
return false;
while (true)
{
// Encurtado:
// Use WaitLatch até alcanzarmos recoveryDelayUntilTime
// e então
break;
}
return true;
}O fato é que a demora é baseada no tempo físico registrado no timestamp do commit da transação (xtime). Como pode ser visto, a demora se aplica apenas a commits e não afeta outros registros — todas as alterações são aplicadas diretamente, enquanto o commit é adiado, assim veremos as alterações apenas após o atraso configurado.
Como usar uma réplica atrasada para recuperação de dados
Vamos supor 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 .
Quando soubemos do problema, nós para a réplica atrasada:
SELECT pg_xlog_replay_pause();Com a pausa não havia risco de a réplica repetir a solicitação DELETE. Uma coisa útil, se você precisar de tempo para investigar tudo.
O ponto é que a réplica atrasada deve chegar ao momento anterior à solicitação DELETE. Sabíamos mais ou menos o tempo físico da exclusão. Nós excluímos recovery_min_apply_delay e adicionamos recovery_target_time em recovery.conf. Assim, a réplica chega ao momento correto sem atrasos:
recovery_target_time = '2018-10-12 09:25:00+00'Com timestamps, é melhor reduzir o que for excessivo, para não errar. Porém, quanto mais reduzirmos, mais dados perdemos. Novamente, se pulássemos a solicitação DELETE, tudo seria excluído novamente e teríamos que começar do zero (ou até mesmo pegar um backup frio para PITR).
Reiniciamos a instância atrasada do Postgres e os segmentos WAL se repetiram até o horário indicado. O progresso nesta fase pode ser acompanhado pela consulta:
SELECT
-- localização atual no WAL
pg_last_xlog_replay_location(),
-- timestamp da transação atual (estado da réplica)
pg_last_xact_replay_timestamp(),
-- tempo físico atual
now(),
-- a quantidade de tempo que ainda precisa ser aplicada até que recovery_target_time seja alcançado
'2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;Se o timestamp não mudar mais, a recuperação está concluída. Você pode configurar a ação , para fechar, avançar ou pausar a instância após a repetição (por padrão, ela é pausada).
O banco de dados voltou ao estado antes daquela consulta infeliz. Agora você pode, por exemplo, exportar dados. Exportamos os dados removidos do atalho e todas as relações com as tarefas e merge requests e os transferimos para o banco de dados ativo. Se as perdas forem significativas, você pode simplesmente avançar a réplica e usá-la como principal. Mas, nesse caso, todas as alterações após o momento até o qual nos recuperamos serão perdidas.
Em vez de timestamps, é melhor usar IDs de transações. É útil registrar esses IDs, por exemplo, para operadores DDL (como DROP TABLE), com o auxílio de log_statements = 'ddl'. Se tivéssemos o ID da transação, pegaríamos recovery_target_xid e executaríamos todos até a transação anterior à consulta. DELETE.
Voltar ao trabalho é muito simples: remova todas as alterações de recovery.conf e reinicie o Postgres. Em breve, a réplica terá novamente um atraso de oito horas, e estaremos prontos para futuros contratempos.
Vantagens para recuperação
Com uma réplica atrasada em vez de um backup frio, não precisamos passar horas restaurando toda a captura do arquivo. Por exemplo, nós precisamos de cinco horas para obter todo o backup base de 2 TB. E depois ainda precisamos aplicar todo o WAL diário para recuperar o estado desejado (no pior cenário).
A réplica atrasada é melhor do que o backup frio por dois motivos:
- Não precisamos retirar todo o backup base do arquivo.
- Há uma janela fixa de oito horas de segmentos WAL que precisam ser repetidos.
Além disso, estamos constantemente verificando se podemos fazer PITR a partir do WAL, e notaríamos rapidamente quaisquer danos ou outros problemas com o arquivo WAL, monitorando o atraso da réplica atrasada.
Neste exemplo, levamos 50 minutos para recuperar, ou seja, a velocidade foi de 110 GB de dados WAL por hora (o arquivo ainda estava em ). No total resolvemos o problema e restauramos os dados em 1,5 horas.
Resultados: onde a replicação atrasada é útil (e onde não é)
Use a replicação atrasada como uma medida de emergência se você acidentalmente perdeu dados e percebeu isso dentro da janela de atraso configurada.
Mas tenha em mente: replicação não é backup.
Backup e replicação têm objetivos diferentes. Um backup frio é útil se você acidentalmente fez DELETE ou DROP TABLE. Fazemos backup do armazenamento frio e restauramos o estado anterior da tabela ou de todo o banco de dados. Mas nesse caso, a consulta DROP TABLE é quase instantaneamente replicada em todas as réplicas do cluster de trabalho, portanto, a replicação comum não salvará a situação. A replicação, por si só, mantém o banco de dados acessível, quando servidores individuais falham, e distribui a carga.
Mesmo com a replicação atrasada, muitas vezes precisamos de um backup frio em um local seguro, caso ocorra uma falha de data center, uma corrupção oculta ou outros eventos que não sejam percebidos imediatamente. Aqui, apenas a replicação não é suficiente.
Nota. No Atualmente, estamos protegendo contra a perda de dados apenas no nível do sistema e não recuperamos os dados no nível do usuário.
Fonte: habr.com
