Problema con el I/O cuando tenemos muchas bases de datos en un mismo servidor
Hace unos dias instalamos un nuevo servidor PostgreSQL 9.2 que se va a utilizar por diferentes cursos de bases de datos que se imparten en la facultad de informática de la Universidad de Oslo.
La idea es que cada alumno registrado en alguno de estos cursos tenga acceso a su base de datos postgreSQL para realizar sus prácticas.
Hasta aquí todo sin problemas, una instalación que sigue nuestros estándares de configuración, backup, seguridad y alta disponibilidad. La única diferencia con respecto a otros sistemas de los que estamos a cargo es el número de bases de datos que estamos ejecutando en el servidor.
Nada más crear las casi 400 bases de datos que necesitabamos y sin empezar a utilizar el servidor, el disco en donde se alojan estas bases de datos empezó a trabajar sin parar.
¿Entre 50MB/s y 60MB/s constantes de escritura sin nadie utilizando el servidor?
Empezamos a investigar y a buscar en Internet si alguien habia tenido el mismo problema y pronto dimos con la explicación.
PostgreSQL mantiene por defecto un fichero en el directorio $PGDATA/pg_stat_tmp/ con información sobre las estadísticas internas de todas las bases de datos en el servidor. Estas estadísticas se pueden configurar con los parámetros definidos en la sección “18.9. Run-time Statistics” de la documentación.
El problema hasta la versión 9.2 es que todas las estadísticas de todas las bases de datos se graban en el mismo fichero. Cuando se tienen muchas bases de datos el proceso “stats collector” accede a este fichero muchas veces por segundo creando una carga I/O considerable en el disco de datos.
En un servidor de pruebas usando postgreSQL 9.2.5, con 500 bases de datos que contienen una simple tabla vacia de dos columnas, obtenemos estos datos de utilización de discos sin ningun usuario conectado al servidor:
Linux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU)
Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn
sdb 184.00 0.00 84.08 0 84
sdb 176.00 0.00 81.47 0 81
sdb 195.00 0.00 91.61 0 91
sdb 161.00 0.00 78.18 0 78
sdb 204.00 0.00 98.00 0 97
sdb 173.00 0.00 82.80 0 82
sdb 171.00 0.00 82.84 0 82
sdb 186.00 0.00 90.01 0 90
sdb 166.00 0.00 80.68 0 80
Datos strace del proceso stats collector despues de aprox. 30 segundos:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.51 1.470643 5978 246 rename
2.01 0.030306 0 642919 write
0.33 0.005000 7 708 poll
0.14 0.002098 8 247 open
0.01 0.000108 0 1941 708 recvfrom
0.00 0.000048 0 246 munmap
0.00 0.000006 0 247 fstat
0.00 0.000000 0 708 708 read
0.00 0.000000 0 246 close
0.00 0.000000 0 247 mmap
0.00 0.000000 0 1 restart_syscall
------ ----------- ----------- --------- --------- ----------------
100.00 1.508209 647756 1416 total
Como podeis ver, con 500 bases de datos el problema se agrava todavía más y producimos sin utilizar el sistema aproxidamente entre 80MB/s y 90MB/s de carga en el disco.
Una solución es empezar a utilizar la reciente versión 9.3 de postgreSQL. Con esta versión la cosa ha mejorado muchisimo al crearse un fichero por base de datos en vez de uno común.
La utilización del disco de datos en vacio disminuye aproximadamente un 98,5% en nuestro ejemplo. Y el número de llamadas write del sistema desciende considerablemente, casi un 99%.
Linux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU)
Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn
sdb 20.00 0.00 1.11 0 1
sdb 20.00 0.00 1.09 0 1
sdb 20.00 0.00 1.09 0 1
sdb 20.00 0.00 1.11 0 1
sdb 22.00 0.00 1.19 0 1
sdb 22.00 0.00 1.21 0 1
sdb 18.00 0.00 0.98 0 0
sdb 20.00 0.00 1.11 0 1
sdb 22.00 0.00 1.21 0 1
Datos strace del proceso stats collector despues de aprox. 30 segundos:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
60.15 0.008014 7 1180 poll
29.63 0.003948 6 622 rename
4.32 0.000576 1 622 open
3.70 0.000493 0 8754 write
1.44 0.000192 0 2524 1181 recvfrom
0.29 0.000038 0 622 mmap
0.27 0.000036 0 622 close
0.16 0.000021 0 622 munmap
0.04 0.000005 0 1181 1181 read
0.00 0.000000 0 622 fstat
0.00 0.000000 0 1 restart_syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.013323 17372 2362 total
Por desgracia no todos podemos actualizar a la versión 9.3 ahora mismo.
La solución que se encuentra en Internet para sistemas menores que la versión 9.3 y que nosotros hemos implementado es crear un sistema de ficheros tmpfs en memoria para no tener que cargar el sistema de almacenamiento con el I/O producido por el proceso “stats collector” de postgres.
Supongo que tmpfs estará disponible por defecto en la mayoria de distribuciones linux de hoy en día. En nuestra Red Hat ELS 6.4 solamente hemos tenido que hacer lo siguiente para empezar a utilizarlo:
Crear un directorio donde montar nuestro sistema de ficheros tmpfs:
mkdir -p /var/lib/pgsql/9.2/pg_stat_tmp
Actualizar el fichero /etc/fstab con esta linea, para crear una partición de 1GB en memoria:
tmpfs /var/lib/pgsql/9.2/pg_stat_tmp tmpfs size=1G,uid=postgres,gid=postgres 0 0
Y ejecutar:
mount /var/lib/pgsql/9.2/pg_stat_tmp
Una vez que tenemos el el area tmpfs configurada y disponible tenemos que actualizar el fichero de configuración postgresql.conf de postgreSQL con la siguiente linea:
stats_temp_directory = '/var/lib/pgsql/9.2/pg_stat_tmp'
Y ponemos este cambio en producción haciendo un reload del servidor postgreSQL.
Después de esto el problema desaparece y tenemos todos los recursos del disco disponible para los usuarios, incluso sin poder utilizar la versión 9.3 todavia.
Linux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU)
Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn
sdb 9.00 0.00 0.14 0 0
sdb 11.00 0.00 0.94 0 0
sdb 9.00 0.00 0.14 0 0
sdb 7.00 0.00 0.11 0 0
sdb 10.00 0.00 0.16 0 0
sdb 7.00 0.00 0.11 0 0
sdb 13.00 0.00 1.28 0 1
sdb 8.00 0.00 0.12 0 0
sdb 9.00 0.00 0.14 0 0
Datos strace del proceso stats collector despues de aprox. 30 segundos:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
94.53 0.404986 1607 252 rename
3.07 0.013168 0 657173 write
2.10 0.008997 13 719 poll
0.22 0.000929 4 253 open
0.03 0.000125 0 719 719 read
0.03 0.000116 0 1981 719 recvfrom
0.02 0.000081 0 252 munmap
0.01 0.000023 0 252 close
0.00 0.000000 0 253 fstat
0.00 0.000000 0 253 mmap
0.00 0.000000 0 1 restart_syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.428425 662108 1438 total
El I/O producido en el disco de datos ha desaparecido aunque el sistema sigue haciendo aproximadamente el mismo número de llamadas write de sistema.