Tengo una instancia de PostgreSQL 9.2 que se ejecuta en RHEL 6.3, máquina de 8 núcleos con 16 GB de RAM. El servidor está dedicado a esta base de datos. Dado que el postgresql.conf predeterminado es bastante conservador con respecto a la configuración de memoria, pensé que podría ser una buena idea permitir que Postgres use más memoria. Para mi sorpresa, seguir consejos en wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server ralentizó significativamente prácticamente todas las consultas que ejecuto, pero obviamente es más notable en las consultas más complejas.
También intenté ejecutar pgtune que dio la siguiente recomendación con más parámetros ajustados, pero eso no cambió nada. Sugiere shared_buffers de 1/4 de tamaño de RAM que parece estar en línea con los consejos en otros lugares (y en PG wiki en particular).
default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80
Intenté reindexar toda la base de datos después de cambiar la configuración (usando reindex database
), pero eso tampoco ayudó. Jugué con shared_buffers y work_mem. Cambiarlos gradualmente de los valores predeterminados muy conservadores (128k / 1MB) disminuyó gradualmente el rendimiento.
Realicé EXPLAIN (ANALYZE,BUFFERS)
algunas consultas y el culpable parece ser que Hash Join es significativamente más lento. No me queda claro por qué.
Para dar un ejemplo específico, tengo la siguiente consulta. Se ejecuta en ~ 2100 ms en la configuración predeterminada y ~ 3300 ms en la configuración con tamaños de búfer aumentados:
select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';
EXPLAIN (ANALYZE,BUFFERS)
para la consulta anterior:
- Búferes predeterminados: http://explain.depesz.com/s/xaHJ
- Buffers más grandes: http://explain.depesz.com/s/Plk
La pregunta es ¿por qué estoy observando un rendimiento disminuido cuando aumento los tamaños de búfer? La máquina definitivamente no se está quedando sin memoria. La asignación si la memoria compartida en el sistema operativo es ( shmmax
y shmall
) se establece en valores muy grandes, eso no debería ser un problema. Tampoco obtengo ningún error en el registro de Postgres. Estoy ejecutando autovacuum en la configuración predeterminada, pero no espero que tenga nada que ver con eso. Todas las consultas se ejecutaron en la misma máquina con unos segundos de diferencia, solo con una configuración modificada (y reiniciando PG).
Editar: acabo de encontrar un hecho particularmente interesante: cuando realizo la misma prueba en mi iMac de mediados de 2010 (OSX 10.7.5) también con Postgres 9.2.1 y 16 GB de RAM, no experimento la desaceleración. Específicamente:
set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms
Cuando hago exactamente la misma consulta (la anterior) con exactamente los mismos datos en el servidor, obtengo 2100 ms con work_mem = 1MB y 3200 ms con 96 MB.
La Mac tiene SSD, por lo que es comprensiblemente más rápido, pero muestra un comportamiento que esperaría.
Consulte también la discusión de seguimiento sobre pgsql-performance .
fuente
Respuestas:
En primer lugar, tenga en cuenta que work_mem es por operación y, por lo tanto, puede volverse excesivo rápidamente. En general, si no tiene problemas con la lentitud, dejaría work_mem solo hasta que lo necesite.
Al observar sus planes de consulta, una cosa que me sorprende es que los resultados del búfer son muy diferentes al observar los dos planes, y que incluso los análisis secuenciales son más lentos. Sospecho que el problema tiene que ver con el almacenamiento en caché de lectura anticipada y tener menos espacio para eso. Lo que esto significa es que está sesgando la memoria para la reutilización de índices y contra la lectura de tablas en el disco.
Tengo entendido que PostgreSQL buscará en la memoria caché una página antes de leerla desde el disco porque no sabe realmente si la memoria caché del sistema operativo contendrá esa página. Debido a que las páginas permanecen en el caché y porque ese caché es más lento que el caché del sistema operativo, esto cambia el tipo de consultas que son rápidas frente a las que son lentas. De hecho, al leer los planes, además de los problemas de work_mem, parece que toda la información de su consulta proviene del caché, pero es una cuestión de qué caché.
work_mem : cuánta memoria podemos asignar para una operación de combinación u ordenación relacionada. Esto es por operación, no por declaración o por back-end, por lo que una sola consulta compleja puede usar muchas veces esta cantidad de memoria. No está claro que esté alcanzando este límite, pero vale la pena señalarlo y tenerlo en cuenta. Si aumenta esto demasiado, pierde memoria que podría estar disponible para la memoria caché de lectura y los búferes compartidos.
shared_buffers : cuánta memoria asignar a la cola real de la página PostgreSQL. Ahora, idealmente, el conjunto interesante de su base de datos permanecerá en la memoria caché aquí y en los búferes de lectura. Sin embargo, lo que esto hace es garantizar que la información utilizada con más frecuencia en todos los servidores se almacene en caché y no se vacíe al disco. En Linux, este caché es significativamente más lento que el caché de disco del sistema operativo, pero ofrece garantías de que el caché de disco del sistema operativo no funciona y es transparente para PostgreSQL. Esto es claramente donde está tu problema.
Entonces, lo que sucede es que cuando tenemos una solicitud, primero verificamos los búferes compartidos ya que PostgreSQL tiene un conocimiento profundo de este caché y buscamos las páginas. Si no están allí, le pedimos al sistema operativo que los abra desde el archivo, y si el sistema operativo ha almacenado en caché el resultado, devuelve la copia en caché (esto es más rápido que los buffers compartidos, pero Pg no puede decir si está en caché o encendido disco, y el disco es mucho más lento, por lo que PostgreSQL generalmente no aprovechará esa oportunidad). Tenga en cuenta que esto también afecta el acceso a la página aleatorio versus secuencial. Por lo tanto, puede obtener un mejor rendimiento con configuraciones más bajas de shared_buffers.
Mi intuición es que probablemente obtenga un mejor rendimiento, o al menos más consistente, en entornos de alta concurrencia con configuraciones más grandes de shared_buffer. También tenga en cuenta que PostgreSQL toma esta memoria y la guarda, por lo que si tiene otras cosas ejecutándose en el sistema, los búferes de lectura mantendrán archivos leídos por otros procesos. Es un tema muy amplio y complejo. La configuración de búfer compartido más grande proporciona mejores garantías de rendimiento, pero en algunos casos puede ofrecer menos rendimiento.
fuente
Además del efecto aparentemente paradójico de que el aumento
work_mem
disminuye el rendimiento ( @Chris podría tener una explicación), puede mejorar su función al menos de dos maneras.LEFT JOIN
conJOIN
. Eso podría confundir al planificador de consultas y conducir a planes inferiores.pi.firstname
ypi.lastname
para admitirLIKE
búsquedas no ancladas . ('%a%'
También se admiten patrones más cortos como , pero no es probable que un índice ayude para predicados no selectivos):O un índice de varias columnas:
Debería hacer su consulta un poco más rápido. Necesita instalar el módulo adicional pg_trgm para esto. Detalles bajo estas preguntas relacionadas:
Además, ¿ha intentado configurar
work_mem
localmente, solo para la transacción actual ?Esto evita que las transacciones concurrentes también consuman más RAM, posiblemente muriéndose de hambre.
fuente