sql >> Base de Datos >  >> RDS >> PostgreSQL

Almacenamiento en caché en PostgreSQL

¡Almacenamiento en caché...!, es un poco difícil ir en resumen con un solo artículo. Pero intentaré compartir mis conocimientos aprendidos de Heikki/Robert Haas/Bruce Momjian en breve. En PostgreSQL, hay dos capas, búferes compartidos de PG y caché de página del sistema operativo, cualquier lectura/escritura debe pasar a través de la caché del sistema operativo (sin pasar por alto hasta ahora). Postgres escribe datos en la memoria caché de la página del sistema operativo y confirma al usuario que ha escrito en el disco, luego la memoria caché del sistema operativo escribe en el disco físico a su propio ritmo. Los búferes compartidos de PG no tienen control sobre la caché de páginas del sistema operativo y ni siquiera saben qué hay en la caché del sistema operativo. Por lo tanto, la mayoría de las recomendaciones dadas por Postgres DBA's/Professional's para tener un DISK más rápido / mejor caché.

Los cachés/búferes en PostgreSQL son más fuertes que otras bases de datos y altamente sofisticados. Como tengo experiencia en Oracle (mentalidad también... :)), entonces, mi pregunta de quién aprendí fue cómo/cuándo/qué/por qué, etc., con respecto a la caché del búfer de la base de datos, los búferes anclados, la caché de los búferes de la base de datos, la precarga de la base de datos, etc. Obtuve todas mis respuestas de ellos, sin embargo, el enfoque es un poco diferente. Aunque mis preguntas eran molestas, respondieron con gran paciencia y me aclararon en gran medida cuál es el resultado de que estás leyendo este blog…. :)..

En algunos aprendizajes (aún aprendiendo), dibujé una pequeña descripción general de cómo fluyen los datos entre la memoria y el disco en Postgres y también algunas de las herramientas importantes y el NUEVO parche de Robert Haas (pg_prewarm) .

pg_buffercache
Un módulo de contribución, que indica qué hay en la memoria caché del búfer de PostgreSQL. Instalación a continuación:-

postgres=# CREATE EXTENSION pg_buffercache;

pgfincore
Tiene una funcionalidad para dar información sobre qué datos en OS Page Cache. Pgfincore, el módulo se vuelve muy útil cuando se combina con pg_buffercache, ahora uno puede obtener la caché del búfer PG y la información de la caché de la página del sistema operativo juntas. Gracias a Cerdic Villemain. Pgfincore, backbone es fadvise, fincore, que son ftools de linux. También puede usar fincore/fadvise instalando source. Dos cosas, puede usar el módulo de contribución pgfincore o ftools, ambos dan el mismo resultado. Probé ambos, son simplemente geniales.

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_precalentamiento
Precargar la relación/índice en el caché del búfer PG. ¿Es posible en PostgreSQL? oh sí, gracias a Robert Haas , que recientemente envió un parche a la comunidad, es de esperar que esté disponible en PG 9.2 o PG 9.3. Sin embargo, puede usar el parche para sus pruebas en PG 9.1.

pg_prewarm tiene tres MODOS:

  1. PRECAUCIÓN: Obtención de bloques de datos de forma asíncrona en la memoria caché del sistema operativo solo, no en los búferes de PG (accede solo a la memoria caché del sistema operativo)
  2. LEER: Lee todos los bloques en un búfer ficticio y los fuerza en la memoria caché del sistema operativo. (accede solo a la memoria caché del sistema operativo)
  3. BÚFER: lee todos los bloques o el rango de bloques en el caché del búfer de la base de datos.

Instalación:
Estoy aplicando el parche pg_prewarm en la instalación de mi fuente PG, debe ajustar según su configuración.

  1. Untar ubicación de la fuente PG:/usr/local/src/postgresql-9.1.3
  2. Ubicación de instalación de PG:/usr/local/pgsql91
  3. Ubicación de todas las descargas:/usr/local/src

Nota:Instale PG antes de aplicar el parche pg_prewarm.

1. Descargue el parche en /usr/local/src/ location
http://archives.postgresql.org/pgsql-hackers/2012-03/binRVNreQMnK4.bin
Correo electrónico adjunto del parche:
http://archives.postgresql.org/message-id/CA+TgmobRrRxCO+t6gcQrw_dJw+Uf9ZEdwf9beJnu+RB5TEBjEw@mail.gmail.com
2. Después de la descarga, vaya a la ubicación de origen de PG y siga los pasos.

# 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. El comando anterior creará archivos en $PGPATH/contrib/extension. Ahora está listo para agregar el 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
Una combinación de herramientas vmstat,iostat,netstat,top,etc., juntas en un comando de Linux "dstat". Cuando la base de datos se comporta de manera inusual, para conocer la causa desde el nivel del sistema operativo, abrimos un par de terminales para extraer el proceso, la memoria, la lectura/escritura del disco, la información de la red, lo cual es un poco molesto para cambiar de ventana. Por lo tanto, dstat tiene varias opciones, lo que ayuda a mostrar todos los comandos en una ventana de salida.

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 de Linux
Está diseñado para trabajar con sistemas Linux modernos, incluidos mincore, fallocate, fadvise, etc. Ftools lo ayudará a descubrir qué archivos se encuentran en la memoria caché del sistema operativo. Con los scripts de perl/python, puede recuperar la información de caché de la página del sistema operativo en los archivos de objetos (pg_class.relfilenode). pg_fincore se basa en esto. Puede usar secuencias de comandos pgfincore o 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.

Ahora, estamos listos para continuar con el ejemplo para verificar con las herramientas y utilidades. En mi ejemplo, tengo una tabla, tiene un índice y una secuencia con más de 100 MB de datos.

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 el tamaño que ocupa la tabla, la secuencia y su í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)

Escribí una pequeña consulta al combinar pgfincore y pg_buffercache para extraer información de PG Buffer &OS Page cache. Usaré esta consulta a lo largo de mi ejemplo, solo pegaré los resultados de esta 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.

Precargar relación/índice usando pg_prewarm:

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 calentar la relación/índice/secuencia usando pg_prewarm y verificar el plan 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)

salida 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 información similar utilizando el script python-ftools, necesita conocer el número de relfilenode de los objetos, verifique a continuación.

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 la secuencia de comandos python-ftools

¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡

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)

¿Cómo vaciar/precalentar la relación/índice en la memoria caché del sistema operativo?
Usando pgfadvise, puede precargar o vaciar la relación del caché del sistema operativo. Para obtener más información, escriba df pgfadvise* en la terminal para todas las funciones relacionadas con pgfadvise. A continuación se muestra un ejemplo de cómo vaciar la memoria caché del sistema operativo.

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)

Mientras estas cosas suceden en una ventana, puede verificar la relación de lectura/escritura usando dstat. Para más opciones, use dstat –list
dstat -s –top-io –top-bio –top-mem

Rango de precarga de bloques usando la funcionalidad de rango pg_prewarm.
Suponga que, por alguna razón, desea rebotar el clúster, pero uno de la tabla grande que está en el búfer está funcionando bien. Al rebotar, su tabla ya no está en los búferes, para volver al estado original como estaba antes del rebote, debe saber cuántos bloques de tabla había en los búferes y precargarlos usando la opción de rango pg_prewarm.

Creé una tabla consultando pg_buffercache y luego envié información de rango de bloques a pg_prewarm. Por esto, los búferes compartidos están de vuelta con la tabla cargada anteriormente. Ver el ejemplo.

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

Haga rebotar el clúster y precargue el rango de bloques relacionados con la tabla en búferes desde "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)

Mira, mi shared_buffer está de nuevo en juego.

Disfrutar…!!! Volveré con más cosas interesantes. Publique sus comentarios.