Tunning - shared_buffers y work_mem

SHARED BUFFERS

El valor shared_buffers es el parámetro que más afecta al rendimiento de PostgreSQL. Este valor, de tipo entero, indica el número de bloques de memoria o buffers de 8KB que Postgres reservará, como zona de trabajo, en el momento del arranque para procesar las consultas. De forma predeterminada (en postgresql.conf), su valor es de 1000. Un número generalmente insuficiente para conseguir un rendimiento mínimamente aceptable.
Estos buffers se ubican dentro de los denominados segmentos de memoria compartida. Es importante saber que el espacio ocupado por el número de buffers que pretendamos asignar, nunca podrá exceder al tamaño máximo que tengan los segmentos de memoria. En caso contrario, Postgres se negará a arrancar avisando con un error que no puede reservar el espacio solicitado.

El proceso para cambiar el parámetro es el siguiente:

  1. Considerar un número superior al actual de shared buffers (comenzaremos por un 10% del total de la memoria)
  2. Modificar el tamaño del segmento si no cabe el número de buffers
  3. Comprobar el rendimiento
  4. En función del resultado obtenido, aumentar o disminuir el porcentaje de memoria y empezar de nuevo
    Ejemplo con 1GB de memoria ram:
    El 10% de 1 GB: (1048576 KB/10) = 104857 KB
    shared_buffers: (104857 KB/8 KB) = 13107
    Nota: A partir de Postgres 8.2, se puede usar notación KB, MB, GB para indicar kilobytes, megabytes y gigabytes respectivamente.
    El 10% de 1 GB: (1048576 KB/10) = 104857 KB
    shared_buffers = 103 MB
    Una vez asignado este valor a la variable shared_buffers (ubicada en postgresql.conf), debemos reiniciar el motor.
    ¿No arranca? Que sucede?
    La respuesta generalmente esta en el archivo de log (o visor de sucesos en windows). Seguramente encontraras algo del estilo:

FATAL: no se pudo crear el segmento de memoria compartida: Argumento inválido
DETALLE: La llamada a sistema fallida fue shmget(key=5432001, size=112009216,03600).
HINT: Este error generalmente significa que una petición de PostgreSQL para obtener un segmento de memoria compartida excedió el parámetro SHMMAX del kernel. Puede reducir el tamaño de la petición o reconfigurar el kernel con un SHMMAX superior. Para reducir el tamaño de la petición (actualmente 112009216 bytes), reduzca el parámetro de PostgreSQL shared_buffers (actualmente 13107) y/o el parámetro max_connections (actualmente 100).

Está claro? el tamaño ocupado por los 13107 buffers que has pedido reservar, simplemente no caben en el tamaño actual de segmento (SHMMAX). Postgres en el arranque intenta reservar este espacio, pero al no poder hacerlo, desiste. ¿Qué podemos hacer?
En Linux, de forma predeterminada, el tamaño de un segmento de memoria compartida es de 32MB. Podemos comprobarlo haciendo (el resultado es en nº de bytes):

$ cat /proc/sys/kernel/shmmax
$ 33554432

Y el espacio que requiere el número de buffers, es superior al tamaño del segmento:
13107 buffers ocupan 107372544 bytes (13107 * 8192 bytes/bloque)
107372544 > 33554432
La solución está en modificar el tamaño máximo del segmento de memoria compartida. Esto lo hacemos asignando un nuevo valor al parámetro del kernel SHMMAX. ¿Qué valor? Si volvemos atrás, al mensaje de error, avisa exactamente de cuál es el tamaño mínimo que postgres necesita para arrancar. El tamaño de SHMMAX debe ser, como mínimo, ese valor.
Podemos cambiar fácilmente el valor usando el programa sysctl:

sysctl -w kernel.shmmax=112009216

Alternativamente, si tu S.O. no dispone de ese programa, puedes usar la forma clásica:

echo 111766938 > /proc/sys/kernel/shmmax

echo 111766938 > /proc/sys/kernel/shmall

Puede guardar este valor de forma permanente en /etc/sysctl.conf, de forma que los cambios se conserven entre arranques: kernel.shmmax=111766938

Luego, reiniciamos Postgres. ¿Funcionó esta vez? ¡Bien!
Haz nuevas pruebas de rendimiento y sigue estos pasos hasta encontrar un valor acorde a hardware disponible y respuesta del sistema.

WORK MEM
Este parámetro configura el espacio de memoria que Postgres utiliza para realizar ordenaciones de tablas o de resultados parciales de consultas, sobre todo en cláusulas ORDER BY, CREATE INDEX o MERGE JOIN (agregación, clausula IN de subqueris)
Este valor es más difícil de configurar porque depende, por un lado, de lo grande que sean las tablas o resultados que hay que ordenar, y por otro, del número de peticiones simultáneas para esa misma consulta (para cada una se empleará la misma cantidad de memoria).
Un buen comienzo es asignar entre un 2% y un 4% del total de la memoria si prevemos pocos accesos simultáneos a grandes sesiones de ordenación y mucho menor, si esperamos muchos accesos simultáneos a sesiones de ordenación pequeñas. Como antes, lo mejor es ir probando distintos valores y ver en qué pueden afectar a la paginación adversa (swap page in). El valor hay que expresarlo en KB.
En nuestro ejemplo, hemos optado por usar un 4% de la memoria:

El 4% de 1 GB: 41943 KB (1048576 KB*4)/100
work_men = 41943
ó
work_men = 40 MB

Fuentes:
http://www.ca.postgresql.org/docs/momjian/hw_performance/
http://developer.postgresql.org/docs/postgres/kernel-resources.html
http://archives.postgresql.org/pgsql-es-ayuda/
http://www.juancarlosmoral.es/postgresql-hardware-tunning/