- Q1. O PG tem a capacidade de armazenar em cache/aquecer uma relação?
- Q2. É possível retornar ao estado anterior do cache onde foi deixado antes de desligar o servidor de banco de dados devido à manutenção?
Nas versões anteriores do PostgreSQL, não havia chance de aquecer uma relação ou armazenar estados de cache, mas a partir do PostgreSQL 9.4 em diante, cada uma das consultas acima (Q1,Q2) tratadas com dois módulos contrib pg_prewarm e pg_hibernator . Apesar do fato de serem distintos em praticidade, no entanto, a combinação parece ser extremamente viável e útil no futuro para DBAs. Resumindo sobre contrib's:
pg_prewarm contrib (Autor:Robert Haas), fornece a capacidade de carregar dados de uma relação no cache de buffer do SO ou no cache de buffer PG. Tem a funcionalidade de primeiro ou último número de bloco para pré-aquecimento. (Nota:Ele não tem proteção especial em dados pré-aquecidos de despejo de cache e também se a instância do banco de dados for reiniciada, será necessário reaquecer as relações).
pg_hibernator contrib (Autor:Gurjeet Singh), fornece a capacidade de salvar automaticamente a lista de conteúdo de buffer compartilhado em disco no desligamento do banco de dados e restaura automaticamente os buffers na inicialização do banco de dados, da mesma forma que salvar/restaurar um instantâneo de shared_buffers. Ele usa o módulo PG 9.3 para registrar “processo de trabalho em segundo plano” e gera dois processos “Buffer Saver”, “Buffer Reader” para salvar/restaurar. Curiosamente, com um pequeno hack, o pg_hibernator também pode permitir que o escravo em espera comece a atender consultas com velocidade máxima com o mesmo conteúdo do mestre, veremos isso em um minuto :).
Por último, precisamos de pg_buffercache módulo para olhar dentro do conteúdo atual de shared_buffers do PostgreSQL. Este módulo ajuda a entender qual porcentagem de buffer está ocupada por uma relação.
Vamos colocar todas essas contribuições em jogo e ver como elas servem ao propósito de duas perguntas (Q1,Q2). Vou usar uma tabela 'foo' de tamanho 885 MB na minha VM local, junto com uma consulta pg_buffercache padrão.
SELECT c.relname,
count(*) AS buffers
FROM pg_class c
INNER JOIN pg_buffercache b ON b.relfilenode=c.relfilenode AND c.relname='foo'
INNER JOIN pg_database d ON (b.reldatabase=d.oid AND d.datname=current_database())
GROUP BY c.relname
ORDER BY 2 DESC LIMIT 10;
Uso de pg_prewarm contrib e aquecimento da tabela 'foo'.
postgres=# criar extensão pg_prewarm;
CRIAR EXTENSÃO
postgres=# dt+
Lista de relações
Esquema | Nome | Tipo | Proprietário | Tamanho | Descrição
--------+------+------+----------+--------+- ------------
público | foo | mesa | postgres | 885 MB |
(1 linha)
postgres=# selecione pg_prewarm('foo');
pg_prewarm
------------
113278
(1 linha)
--pg_buffercache saída da consulta
relname | buffers
---------+---------
foo | 113278
(1 linha)
Uso muito simples e direto do pg_prewarm com uma saída de blocos aquecidos em shared_buffers para a relação ‘foo’. De pg_buffercache resultado da consulta, podemos avaliar que existem 113278 (113278 * 8 / 1024 =884 MB ) buffers de tamanho de bloco de 8 KB da relação 'foo' que corresponde à saída pg_prewarm. Aqui, se o servidor Postgres reiniciar por algum motivo, os shared_buffers estão vazios e os DBAs precisam reaquecer novamente para voltar ao estágio de aquecimento anterior. Para uma única mesa, o reaquecimento é sempre simples, exceto para um grupo de mesas, sua agonia.
Neste ponto, podemos fazer uso do pg_hibernator contrib, pois ele tem a flexibilidade de salvar o conteúdo do shared_buffer e restaurá-lo na inicialização. Vamos habilitar pg_hibernator/pg_prewarm juntos e executar um exercício semelhante simplesmente incluindo uma etapa de reinicialização e ver se o estado do cache retorna como está ou não. Não vou cobrir a instalação do pg_hibernator, porque no git está muito bem descrito, no entanto, eu pularia diretamente para a parte de implementação e iniciaria o servidor com pg_hibernator.
postgres 24623 1 0 02:06 pts/4 00:00:00 /usr/local/pgpatch/pg/bin/postgres -D /usr/local/pgpatch/pg/data_10407
postgres 24627 24623 0 02:06 ? 00:00:00 postgres:processo do registrador
postgres 24631 24623 0 02:06 ? 00:00:00 postgres:processo de checkpoint
postgres 24632 24623 0 02:06 ? 00:00:00 postgres:processo de gravação
postgres 24633 24623 0 02:06 ? 00:00:00 postgres:wal writer process
postgres 24634 24623 0 02:06 ? 00:00:00 postgres:processo do lançador de autovacuum
postgres 24635 24623 0 02:06 ? 00:00:00 postgres:processo de arquivamento
postgres 24636 24623 0 02:06 ? 00:00:00 postgres:processo do coletor de estatísticas
postgres 24637 24623 0 02:06 ? 00:00:00 postgres:bgworker:Buffer Saver
postgres 24638 24623 11 02:06 ? 00:00:01 postgres:bgworker:Block Reader 2
Nos logs do servidor de banco de dados na inicialização:
-bash-4.1$ mais postgresql-2014-06-02_083033. log
LOG:o sistema de banco de dados foi desligado em 2014-06-02 08:13:00 PDT
LOG:iniciando o processo de trabalho em segundo plano "Buffer Saver"
LOG:o sistema de banco de dados está pronto para aceitar conexões
LOG:iniciador de autovacuum iniciado
Desde a primeira vez que o pg_hibernator está em jogo, você pode ver dois processos e também logs com algumas informações sobre o início do “Buffer Saver”. Agora, vamos pré-aquecer a relação 'foo' e reiniciar o servidor, depois verificar o status do buffer se pg_hibernator preencheu o buffer de volta onde foi deixado.
-bash-4.1$ psql -p 10407
psql (9.4beta1)
Digite "help" para obter ajuda.
postgres=# selecione pg_prewarm('foo');
pg_prewarm
------------
113278
(1 linha)
--pg_buffercache saída da consulta
relname | buffers
---------+---------
foo | 113278
(1 linha)
postgres=# q
-bash-4.1$ /usr/local/pgpatch/pg/bin/pg_ctl -D /usr/local/pgpatch /pg/data_10407 stop
aguardando o desligamento do servidor.... feito
servidor parado
-bash-4.1$ ls -l $PGDATA/pg_hibernator/
total 12
-rw----------- 1 postgres postgres 160 Jun 3 01:41 1.global.save
-rw----------- 1 postgres postgres 915 Jun 3 01 :41 2.postgres.save
-bash-4.1$ /usr/local/pgpatch/pg/bin/pg_ctl -D /usr/local/pgpatch/pg/data_10407 iniciar
servidor começando
Reiniciamos o servidor de banco de dados, vamos examinar os logs
-bash-4.1$ mais postgresql-2014-06-03_020601.log
LOG:sistema de banco de dados foi desligado em 2014-06-03 02:05:57 PDT
LOG:iniciando trabalho em segundo plano processo "Buffer Saver"
LOG:sistema de banco de dados está pronto para aceitar conexões
LOG:iniciador de autovacuum iniciado
LOG:registrando trabalho em segundo plano "Block Reader 2"
LOG:iniciando trabalho em segundo plano processo "Block Reader 2"
LOG:Block Reader 2:restaurado 113433 blocos
LOG:Block Reader 2:todos os blocos lidos com sucesso
LOG:worker process:Block Reader 2 (PID 24638) encerrado com o código de saída 1
LOG:cancelando o registro do trabalho em segundo plano "Block Reader 2"
LOG:registrando o trabalho em segundo plano "Block Reader 1"
LOG:iniciando o processo de trabalho em segundo plano "Block Reader 1"
LOG:Block Reader 1:restaurado 20 blocos
LOG:Block Reader 1:todos os blocos lidos com sucesso
LOG:processo de trabalho:Block Reader 1 (PID 24664) encerrado com código de saída 1
LOG :cancelando o registro do trabalhador em segundo plano "Bloquear leitura er 1"
Assim, “Buffer Reader” restaurou blocos de 113433 + 20, dos quais 113278 pertencem à relação ‘foo’. Ótimo, vamos conectar e ver.
-bash-4.1$ psql -p 10407
psql (9.4beta1)
Digite "help" para obter ajuda.
--pg_buffercache saída da consulta
relname | buffers
---------+---------
foo | 113278
(1 linha)
Legal… pg_hibernator trouxe de volta o estado de cache aquecido sem a interferência do DBA.
Outra coisa boa sobre o pg_hibernator, um standby recém-criado pode ter o mesmo conteúdo de buffer compartilhado que o master, para que o standby possa começar a atender consultas a toda velocidade. Para fazer este exercício, enquanto fazia um backup do diretório $PGDATA, passei SIGTERM para o processo “Buffer Saver” para que ele gravasse o conteúdo de shared_buffers do estado atual no disco (diretório $PGDATA/pg_hibernator) e depois segui com a configuração de espera.
postgres 24637 24623 0 02:06 ? 00:00:00 postgres:bgworker:Buffer Saver
postgres 24653 15179 0 02:06 ? 00:00:01 postgres:wal receiver processa streaming 1/6A000A10
postgres 24654 24623 0 02:06 ? 00:00:00 postgres:wal sender process postgres ::1(65011) streaming 1/6A000A10
Após a configuração, meu escravo começou com o mesmo conteúdo do primário
-bash-4.1$ psql -p 10477
psql (9.4beta1)
Digite "help" para obter ajuda.
postgres=# selecione pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 linha)
--pg_buffercache saída da consulta
relname | buffers
---------+---------
foo | 113278
(1 linha)
Obrigado a ambos os autores por uma extensão maravilhosa em cache.