La verdad y la mentira de los tablespaces en memoria
por Emanuel Calvo Franco
Tablespaces en RAM (Linux)
Se agradece la colaboración de Jose Vazquez.
Este artículo está sujeto a cambios
Últimamente se manejó mucho el termino de Bases de Datos en Memoria. Si bien existen versiones de Postgres que tienen esta forma de trabajo (FastDb es una de ellas), puede resultar interesante jugar con un pequeño servidor que tengamos para pruebas.
En este pequeño artículo mostraremos como hacerlo de manera básica y práctica, junto con algunos resultados. cabe destacar que las pruebas fueron realizadas sobre una maquina virtual, por lo que pueden variar mucho en otros ambientes más dedicados.
En general, este tipo de seteos (en mi experiencia) se han dado en servidores OLAP ya que requieren una buena capacidad de procesamiento. Además, por lo general, son servidores separados del cluster de datos, por lo que se los utilza como servidores esclavos de una replicación y cuyos datos son sencillamente recuperables en caso de un abrupto apagado.
Esta técnica permitiría de alguna manera, ocupar ese espacio con objetos como tablas temporales, vistas materializadas, etc.
Primero, para facilitarnos las cosas, debemos enviar una parametro al kernel, del tamaño de los discos en memoria. En este caso los estamos setendo en 4 GB, pero quizás, para hcer más ameno el manejo, nos gustaría armarlos de menos (2 o 1 GB):
kernel /vmlinuz-2.6.28-13-server root=UUID=5ac65858-cdf5-4c26-8557-220563f52901 ro quiet splash ramdisk_size=4194304
Si queremos esto permanente, podríamos tocar el sysctl.conf:
kernel.ramdisk_size=4194304
Desde el shell, al inicio del sistema operativo:
mke2fs /dev/ram0 mkdir /mnt/ram0 mount /dev/ram0 /mnt/ram0 mkdir /mnt/ram0/pgspace chown postgres:postgres /mnt/ram0/pgspace
Con esto anterior formateamos un espacio en RAM y lo montamos en un directorio de la tabla de particiones. Cabe destacar que la herramienta que utilizamos aca es para formatear con el ext2. Podría, depende la situación, utilizar otra partición.
Ahora nos queda por trabajar con la base de datos:
ubuntu=# CREATE TABLESPACE ram_space LOCATION '/mnt/ram0/pgspace'; CREATE TABLESPACE ubuntu=# create table test_ram (i integer, name text) tablespace ram_space; CREATE TABLE ubuntu=# create temp table test_ram_temp (i integer, name text) tablespace ram_space; CREATE TABLE ubuntu=# create temp table test_disk_temp (i integer, name text); CREATE TABLE ubuntu=# create table test_disk (i integer, name text); CREATE TABLE
Hicimos un Tablespace en ese directorio y luego creamos 2 tipos de tablas en 2 ubicaciones combinables: Temporales-Estandar y En Ram-En disco.
Veamos los resultados parciales:
ubuntu=# explain analyze insert into test_ram values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.019..9354.014 rows=1000000 loops=1) Total runtime: 22836.532 ms (2 rows) ubuntu=# explain analyze insert into test_ram_temp values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.025..7507.349 rows=1000000 loops=1) Total runtime: 12773.371 ms (2 rows) ubuntu=# explain analyze insert into test_disk values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.025..7948.205 rows=1000000 loops=1) Total runtime: 16902.042 ms (2 rows) ubuntu=# explain analyze insert into test_disk_temp values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.018..8135.287 rows=1000000 loops=1) Total runtime: 13716.049 ms (2 rows)
Lo que hicimos fue, un insert con serie sumado a la generación de un numero aleatorio casteándolo a texto. En estas pequeñas pruebas insertamos 1.000.000 de registros (lo cual no es poco!).
Veamos el resumen:
Tabla estandar en ram: 22836.532 Tabla estandar en disco: 16902.042
tabla temporal en ram: 12773.371 tabla temporal en disco: 13716.049
Puede resultar que en tu máquina, los resultados sean distintos. En este caso lo hice con una máquina virtual (VBox) con un PGLive (Pgsql 8.3.5).
Ideas
A prima facie, vemos que varia en el manejo de tipo de tabla (más allá de donde esté alojada). esto puede ser algo determinante a la hora de elegir.
Quizás se puede combinar con una Vista Materializada en memoria y de tipo temporal.
Otra cosa a tener en cuenta es que recuerden que cuando inicie el sistema operativo, borrará el contenido del tablespace, por lo que esta técnica solo serviría si usaramos tablas temporales.Inclusive si tuviesemos una actualización desde estas a las del disco, este sistema de trabajo es inconsistente, por lo que siempre es conveniente grabar en disco y luego actualizar en memoria.
Ideas 2
Pero, existe algo que proponen en http://www.linux.com/feature/142658. Y es utilizar un SSD para almacenar los indices.
Esto es mucho más coherente y no es nada nuevo, ya que siempre se acostumbra a utilizar los medios más rápidos para almacenar los índices. Además, en el caso de este artículo, si hay un corte abrupto del suministro o caída del servidor, lo único que deberiamos hacer es reindizar.
En el caso del artículo referenciado, este propone utilizar XFS para almacenar los índices. En mi opinión se debería usar el sistema de ficheros más liviano que nuestro sistema operativo soporte, ya que buscanmos velocidad y no seguridad.
Podemos crear índices en memoria (on en un Pen o un SSD) así:
create index indice_ on tabla_ ( i ) tablespace ram_space;
Ideas 3
Otras de las ideas es, utilizar un Pen Drive para almacenar la WAL. Esto se puede hacer relativamente sencillo, creando un link simbólico llamado pg_xlog que apunte al dispositivo en cuestión (dentro del PGDATA encontraremos este directorio). Copien el contenido del pg_xlog original en el Pen Drive (o muevan), crean el link y listo.
La idea es muy similar a los tablespaces en memoria pero con la gran diferencia de que hacerlo con la WAL es peligroso. Una corrupcíon en la WAL podría generar varios dolores de cabeza, por lo que una caída del servidor nos dejaría sin WAL!! (a menos que tengamos un backup sincronizado).
Para solventar la inestabilidad que puede ocacionarse en un Pen Drive (ya sea porque son más frágiles o por cualquier cosa que pueda cooperar a perder la WAL), es sincronizar el contenido del Pen en otro disco. De esa manera el servidor Postgres escribirá sobre el Pen pero el sistema operativo realizará un Backup del contenido.
Para la WAL necesitamos como mínimo un sistema de ficheros con log (ext3) por ejemplo.
Ideas 4
En conjunto con la comunidad del AOSUG (Open Solaris - www.aosug.com.ar), estamos viendo la forma de crear dispositivos de almacenamiento en memoria administrados con ZFS. De esta manera, las características que se añadirían serían de muchísima utilidad.