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

Cache no PostgreSQL

Cache…!!, é um pouco difícil ir em breve com um único artigo. Mas tentarei compartilhar meu conhecimento aprendido com Heikki / Robert Haas / Bruce Momjian resumidamente. No PostgreSQL, existem duas camadas, buffers compartilhados PG e cache de página do sistema operacional, qualquer leitura/gravação deve passar pelo cache do sistema operacional (sem desvio até agora). O Postgres grava dados no cache da página do sistema operacional e confirma ao usuário como foi gravado no disco, depois o cache do sistema operacional grava no disco físico em seu próprio ritmo. Os buffers compartilhados do PG não têm controle sobre o cache da página do sistema operacional e nem sabem o que está no cache do sistema operacional. Assim, a maioria das recomendações dadas pelos DBA's/Profissionais do Postgres para ter um DISK mais rápido/melhor cache.

Caches/buffers no PostgreSQL são mais fortes como outros bancos de dados e altamente sofisticados. Como sou do fundo Oracle (mindset também…:) ), então, minha pergunta é de quem eu aprendi foi como / quando / o que / por que etc., em relação ao cache de buffer do banco de dados, buffers fixados, cache de buffers de banco de dados de descarga, banco de dados pré-carregado etc., Eu tenho todas as minhas respostas deles, no entanto, a abordagem é um pouco diferente. Embora minhas perguntas estivessem incomodando, elas responderam com muita paciência e me esclareceram em boa medida, o que significa que você está lendo este blog…. :)..

Em alguns aprendizados (ainda aprendendo), desenhei uma pequena visão geral de como os dados fluem entre a memória para o disco no Postgres e também algumas das ferramentas importantes e o NOVO patch de Robert Haas(pg_prewarm) .

pg_buffercache
Um módulo contrib, que informa o que está no cache de buffer do PostgreSQL. Instalação abaixo:-
postgres=# CREATE EXTENSION pg_buffercache;

pgfincore
Tem uma funcionalidade para fornecer informações sobre quais dados no Cache da Página do SO. Pgfincore, o módulo torna-se muito útil quando é combinado com pg_buffercache, agora é possível obter informações de cache de buffer PG e cache de página do SO juntos. Obrigado a Cerdic Villemain. Pgfincore, backbone é fadvise, fincore que são ftools linux. Você também pode usar fincore/fadvise instalando source. Duas coisas, você pode usar o módulo contrib pgfincore ou ftools ambos resultam da mesma forma. Eu tentei os dois, eles são simplesmente incríveis.
Installation:
Download the latest version: http://pgfoundry.org/frs/download.php/3186/pgfincore-v1.1.1.tar.gz
As root user:
export PATH=/usr/local/pgsql91/bin:$PATH //Set the path to point pg_config.
tar -xvf pgfincore-v1.1.1.tar.gz
cd pgfincore-1.1.1
make clean
make
make install

Now connect to PG and run below command

postgres=# CREATE EXTENSION pgfincore;

pg_prewarm
Pré-carregando a relação/índice no cache do buffer PG. É possível no PostgreSQL? ah sim, obrigado a Robert Haas , que recentemente enviou um patch para a comunidade, espero que esteja disponível no PG 9.2 ou PG 9.3. No entanto, você pode usar o patch para seus testes no PG 9.1.

pg_prewarm tem três MODEs:
  1. PREFERÊNCIA: Buscando blocos de dados de forma assíncrona apenas no cache do SO, não nos buffers PG (atinge apenas o cache do SO)
  2. LEIA: Lê todos os blocos no buffer fictício e força no cache do SO. (atinge apenas o cache do SO)
  3. AMORTECEDOR: lê todos os blocos ou intervalo de blocos no cache de buffer do banco de dados.

Instalação:
Estou aplicando o patch pg_prewarm na minha instalação de origem do PG, você precisa ajustar de acordo com sua configuração.
  1. Untar localização da fonte PG:/usr/local/src/postgresql-9.1.3
  2. Local de instalação do PG:/usr/local/pgsql91
  3. Local de todos os downloads:/usr/local/src

Nota:Instale o PG antes de aplicar o patch pg_prewarm.

1. Baixe o patch para /usr/local/src/ location
http://archives.postgresql.org/pgsql-hackers/2012-03/binRVNreQMnK4.bin
E-mail anexado ao patch:
http://archives.postgresql.org/message-id/CA+TgmobRrRxCO+t6gcQrw_dJw+Uf9ZEdwf9beJnu+RB5TEBjEw@mail.gmail.com
2. Após o download, vá para o local de origem do PG e siga as etapas.
# cd /usr/local/src/postgresql-9.1.3
# patch -p1 < ../pg_prewarm.bin         (I have renamed after download)
# make -C contrib/pg_prewarm
# make -C contrib/pg_prewarm install

3. O comando acima criará arquivos em $PGPATH/contrib/extension. Agora você está pronto para adicionar o módulo contrib.
postgres=# create EXTENSION pg_prewarm;
CREATE EXTENSION
postgres=# dx
List of installed extensions
Name | Version | Schema | Description
----------------+---------+------------+----------------------------------------
pg_buffercache | 1.0 | public | examine the shared buffer cache
pg_prewarm | 1.0 | public | prewarm relation data
pgfincore | 1.1.1 | public | examine and manage the os buffer cache
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(4 rows)

Documentation:
/usr/local/src/postgresql-9.1.3/doc/src/sgml
[root@localhost sgml]# ll pgpre*
-rw-r--r-- 1 root root 2481 Apr 10 10:15 pgprewarm.sgml

dstat
Uma combinação de vmstat,iostat,netstat,top,etc., ferramentas juntas em um comando linux “dstat”. Quando o banco de dados se comporta de maneira incomum, para saber a causa do nível do sistema operacional, abrimos alguns terminais para puxar processo, memória, leitura / gravação de disco, informações de rede, o que é um pouco difícil de embaralhar entre as janelas. Portanto, o dstat possui várias opções de servidor, o que ajuda a mostrar todos os comandos em uma saída em uma janela.
Installation:
Dstat download link: (RHEL 6)
wget http://pkgs.repoforge.org/dstat/dstat-0.7.2-1.el6.rfx.noarch.rpm
or
yum install dstat
Documentation: http://dag.wieers.com/home-made/dstat/

Ftools do Linux
É projetado para trabalhar com chamadas de sistema linux modernas, incluindo mincore, fallocate, fadvise, etc. Ftools, irá ajudá-lo a descobrir quais arquivos estão no cache do sistema operacional. Usando scripts perl/python, você pode recuperar informações de cache da página do SO em arquivos de objetos (pg_class.relfilenode). pg_fincore é baseado nisso. Você pode usar scripts pgfincore ou ftools.
Installation:
Download the tar.gz from the link.
https://github.com/david415/python-ftools

cd python-ftools
python setup.py build
export PYTHONPATH=build/lib.linux-x86_64-2.5
python setup.py install

Note: You need to have python & psycopg2 installed before installing python-ftools.

Agora, estamos prontos para prosseguir com o exemplo para verificar as ferramentas e utilitários. No meu exemplo, eu tenho uma tabela, ela tem um índice e sequência com mais de 100 MB de dados.
postgres=# d+ cache
Table "public.cache"
Column | Type | Modifiers | Storage | Description
--------+---------+-----------------------------------------+----------+-------------
name | text | | extended |
code | integer | | plain |
id | integer | default nextval('icache_seq'::regclass) | plain |
Indexes:
"icache" btree (code)
Has OIDs: no

Consulta para saber o tamanho ocupado pela tabela, sequência e seu índice.
postgres=# SELECT c.relname AS object_name,
CASE when c.relkind='r' then 'table'
when c.relkind='i' then 'index'
when c.relkind='S' then 'sequence'
else 'others'
END AS type,pg_relation_size(c.relname::text) AS size, pg_size_pretty(pg_relation_size(c.relname::text)) AS pretty_size
FROM pg_class c
JOIN pg_roles r ON r.oid = c.relowner
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE (c.relkind = ANY (ARRAY['r'::"char", 'i'::"char", 'S'::"char",''::"char"])) AND n.nspname = 'public';

object_name | type | size | pretty_size
-------------+----------+----------+-------------
icache_seq | sequence | 8192 | 8192 bytes
cache | table | 83492864 | 80 MB
icache | index | 35962880 | 34 MB
(3 rows)

Total object size 'cache'

postgres=# select pg_size_pretty(pg_total_relation_size('cache'));
pg_size_pretty
----------------
114 MB
(1 row)

Eu escrevi uma pequena consulta batendo pgfincore e pg_buffercache para extrair informações do cache PG Buffer &OS Page. Estarei usando essa consulta em meu exemplo, apenas colando as saídas dessa consulta.
select rpad(c.relname,30,' ') as Object_Name,
case when c.relkind='r' then 'Table' when c.relkind='i' then 'Index' else 'Other' end as Object_Type,
rpad(count(*)::text,5,' ') as "PG_Buffer_Cache_usage(8KB)",
split_part(pgfincore(c.relname::text)::text,','::text,5) as "OS_Cache_usage(4KB)"
from pg_class c inner join pg_buffercache b on b.relfilenode=c.relfilenode
inner join pg_database d on (b.reldatabase=d.oid and d.datname=current_database() and c.relnamespace=(select oid from pg_namespace where nspname='public'))
group by c.relname,c.relkind
order by "PG_Buffer_Cache_usage(8KB)"
desc limit 10;

object_name | object_type | PG_Buffer_Cache_usage(8KB) | OS_Cache_usage(4KB)
-------------+-------------+----------------------------+---------------------
(0 rows)

Note: I have bounced the cluster to flush PG buffers & OS Page Cache. So, no data in any Cache/buffer.

Pré-carregando relação/índice usando pg_prewarm:
Antes, saltando o cluster eu acionei uma consulta de varredura sequencial de tabela completa na tabela “Cache”, e observei o tempo que está antes de aquecer a relação/índice.
postgres=# explain analyze select * from cache ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Seq Scan on cache (cost=0.00..26192.00 rows=1600000 width=19) (actual time=0.033..354.691 rows=1600000 loops=1)
Total runtime: 427.769 ms
(2 rows)

Permite aquecer relação/índice/sequência usando pg_prewarm e verificar o plano de consulta.
postgres=# select pg_prewarm('cache','main','buffer',null,null);
pg_prewarm
------------
10192
(1 row)
postgres=# select pg_prewarm('icache','main','buffer',null,null);
pg_prewarm
------------
4390
(1 row)

Output of combined buffers:
object_name | object_type | PG_Buffer_Cache_usage(8KB) | OS_Cache_usage(4KB)
-------------+-------------+----------------------------+---------------------
icache | Index | 4390 | 8780
cache | Table | 10192 | 20384
(2 rows)

saída do pgfincore:
postgres=# select relname,split_part(pgfincore(c.relname::text)::text,','::text,5) as "In_OS_Cache" from pg_class c where relname ilike '%cache%';
relname | In_OS_Cache
------------+-------------
icache_seq | 2
cache | 20384
icache | 8780
(3 rows)

or for each object.

postgres=# select * from pgfincore('cache');
relpath | segment | os_page_size | rel_os_pages | pages_mem | group_mem | os_pages_free | databit
------------------+---------+--------------+--------------+-----------+-----------+---------------+---------
base/12780/16790 | 0 | 4096 | 20384 | 20384 | 1 | 316451 |
(1 row)

Para recuperar informações semelhantes usando o script python-ftools, você precisa saber o número do relfilenode dos objetos, verifique abaixo.
postgres=# select relfilenode,relname from pg_class where relname ilike '%cache%';
relfilenode | relname
-------------+----------------
16787 | icache_seq /// you can exclude sequence.
16790 | cache /// table
16796 | icache /// index
(3 rows)

usando o script python-ftools

Não é interessante...!!!!.
Agora compare o plano de explicação após o aquecimento da tabela no buffer.
postgres=# explain analyze select * from cache ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Seq Scan on cache (cost=0.00..26192.00 rows=1600000 width=19) (actual time=0.016..141.804 rows=1600000 loops=1)
Total runtime: 215.100 ms
(2 rows)

Como liberar/pré-aquecer relação/índice no cache do SO?
Usando o pgfadvise, você pode pré-carregar ou liberar a relação do cache do SO. Para obter mais informações, digite df pgfadvise* no terminal para todas as funções relacionadas ao pgfadvise. Abaixo está um exemplo de liberação do cache do SO.
postgres=# select * from pgfadvise_dontneed('cache');
relpath | os_page_size | rel_os_pages | os_pages_free
------------------+--------------+--------------+---------------
base/12780/16790 | 4096 | 20384 | 178145
(1 row)
postgres=# select * from pgfadvise_dontneed('icache');
relpath | os_page_size | rel_os_pages | os_pages_free
------------------+--------------+--------------+---------------
base/12780/16796 | 4096 | 8780 | 187166
(1 row)
postgres=# select relname,split_part(pgfincore(c.relname::text)::text,','::text,5) as "In_OS_Cache" from pg_class c where relname ilike '%cache%';
relname | In_OS_Cache
------------+-------------
icache_seq | 0
cache | 0
icache | 0
(3 rows)

Enquanto essas coisas estão acontecendo em uma janela, você pode verificar a proporção de leitura/gravação usando dstat. Para mais opções, use dstat –list
dstat -s –top-io –top-bio –top-mem

Pré-carregando o intervalo de blocos usando a funcionalidade de intervalo pg_prewarm.
Assuma que, por algum motivo, você deseja devolver o cluster, mas uma das grandes tabelas que está no buffer está funcionando bem. Ao saltar, sua tabela não está mais em buffers, para voltar ao estado original como estava antes de saltar, você precisa saber quantos blocos de tabela estavam nos buffers e pré-carregá-los usando a opção de intervalo pg_prewarm.

Criei uma tabela consultando pg_buffercache e depois enviei informações de intervalo de bloco para pg_prewarm. Com isso, os buffers compartilhados estão de volta com a tabela carregada anteriormente. Veja o exemplo.
select c.relname,count(*) as buffers from pg_class c 
inner join pg_buffercache b on b.relfilenode=c.relfilenode and c.relname ilike '%cache%'
inner join pg_database d on (b.reldatabase=d.oid and d.datname=current_database())
group by c.relname
order by buffers desc;
relname | buffers
---------+---------
cache | 10192
icache | 4390
(2 rows)
Note: These are the blocks in buffer.

postgres=# create table blocks_in_buff (relation, fork, block) as select c.oid::regclass::text, case b.relforknumber when 0 then 'main' when 1 then 'fsm' when 2 then 'vm' end, b.relblocknumber from pg_buffercache b, pg_class c, pg_database d where b.relfilenode = c.relfilenode and b.reldatabase = d.oid and d.datname = current_database() and b.relforknumber in (0, 1, 2);
SELECT 14716

Bounce o cluster e pré-carregue o intervalo de blocos relacionados à tabela em buffers do “blocks_in_buff”.
postgres=# select sum(pg_prewarm(relation, fork, 'buffer', block, block)) from blocks_in_buff;
sum
-------
14716
(1 row)

postgres=# select c.relname,count(*) as buffers from pg_class c
inner join pg_buffercache b on b.relfilenode=c.relfilenode and c.relname ilike '%cache%'
inner join pg_database d on (b.reldatabase=d.oid and d.datname=current_database())
group by c.relname
order by buffers desc;
relname | buffers
---------+---------
cache | 10192
icache | 4390
(2 rows)

Veja, meu shared_buffer está de volta ao jogo.

Aproveitar…!!! estará de volta com coisas mais interessantes. Poste seus comentários.