Optimizar PostgreSQL para pruebas rápidas

203

Estoy cambiando a PostgreSQL desde SQLite para una aplicación típica de Rails.

El problema es que las especificaciones de ejecución se volvieron lentas con PG.
En SQLite tardó ~ 34 segundos, en PG es ~ 76 segundos, que es más de 2 veces más lento .

Así que ahora quiero aplicar algunas técnicas para llevar el rendimiento de las especificaciones a la par con SQLite sin modificaciones de código (idealmente solo configurando las opciones de conexión, lo que probablemente no sea posible).

Un par de cosas obvias desde lo alto de mi cabeza son:

  • Disco RAM (sería bueno ver una buena configuración con RSpec en OSX)
  • Tablas sin registrar (¿se puede aplicar en toda la base de datos para que no haya cambiado todos los scripts?)

Como habrás entendido, no me importa la confiabilidad y el resto (el DB es solo una cosa desechable aquí).
Necesito aprovechar al máximo el PG y hacerlo lo más rápido posible .

La mejor respuesta ideal sería describir los trucos para hacer exactamente eso, la configuración y los inconvenientes de esos trucos.

ACTUALIZACIÓN: fsync = off + full_page_writes = offsolo disminuyó el tiempo a ~ 65 segundos (~ -16 segundos). Buen comienzo, pero lejos del objetivo de 34.

ACTUALIZACIÓN 2: Me trató de disco uso de RAM , pero la ganancia de rendimiento estaba dentro de un margen de error. Así que no parece valer la pena.

ACTUALIZACIÓN 3: * Encontré el mayor cuello de botella y ahora mis especificaciones se ejecutan tan rápido como las SQLite.

El problema fue la limpieza de la base de datos que hizo el truncamiento . Aparentemente, SQLite es demasiado rápido allí.

Para "arreglarlo", abro una transacción antes de cada prueba y la vuelvo al final.

Algunos números para ~ 700 pruebas.

  • Truncamiento: SQLite - 34s, PG - 76s.
  • Transacción: SQLite - 17s, PG - 18s.

Aumento de velocidad 2x para SQLite. Aumento de velocidad 4x para PG.

Dmytrii Nagirniak
fuente
2
Realmente dudo que consigas que funcione tan rápido como SQLite. SQLite con un solo usuario es increíblemente rápido. El diseño de SQLite es muy rápido con pocos usuarios y escalas deficientes; El diseño de Pg escala bien, pero no es tan rápido para un trabajo masivo simple con un solo usuario.
Craig Ringer
1
Me doy cuenta de eso, pero hay un caso particular en el que espero optimizar PG para (ejecuciones de prueba) para que sea lo más rápido posible. No me importa que sea un poco más lento allí, pero 2.2x es demasiado lento. ¿Ves lo que quiero decir?
Dmytrii Nagirniak
+1 Me interesarían mucho las actualizaciones sobre el enfoque del disco RAM si tiene algún resultado al respecto.
tscho
@tscho Definitivamente lo publicaré aquí. Pero necesito algo de tiempo ya que estoy trabajando en otras cosas e "investigando" las cosas de PG en el "fondo".
Dmytrii Nagirniak
¿Está insertando los datos su problema o consulta ? No está claro por su pregunta.
a_horse_with_no_name

Respuestas:

281

Primero, use siempre la última versión de PostgreSQL. Las mejoras de rendimiento siempre están llegando, por lo que probablemente esté perdiendo el tiempo si está ajustando una versión anterior. Por ejemplo, PostgreSQL 9.2 mejora significativamente la velocidadTRUNCATE y, por supuesto, agrega escaneos de solo índice. Incluso los lanzamientos menores siempre deben seguirse; ver la política de versiones .

No hacer

No NO poner un espacio de tabla en un disco RAM u otro almacenamiento no duraderos .

Si pierde un espacio de tabla, toda la base de datos puede dañarse y ser difícil de usar sin un trabajo significativo. Esto tiene muy poca ventaja en comparación con solo usar UNLOGGEDtablas y tener mucha RAM para caché de todos modos.

Si realmente desea un sistema basado en ramdisk, initdbun clúster completamente nuevo en el ramdisk initdbincorporando una nueva instancia de PostgreSQL en el ramdisk, por lo que tiene una instancia de PostgreSQL completamente desechable.

Configuración del servidor PostgreSQL

Al realizar la prueba, puede configurar su servidor para una operación no duradera pero más rápida .

Este es uno de los únicos usos aceptables para la fsync=offconfiguración en PostgreSQL. Esta configuración casi le dice a PostgreSQL que no se moleste con las escrituras ordenadas o cualquiera de esas otras cosas desagradables de protección de integridad de datos y seguridad contra choques, lo que le da permiso para destruir completamente sus datos si pierde energía o tiene un bloqueo del sistema operativo.

Huelga decir que nunca debe habilitar la fsync=offproducción a menos que esté usando Pg como una base de datos temporal para datos que puede volver a generar desde otro lugar. Si y solo si está haciendo para desactivar fsync también se puede full_page_writesdesactivar, ya que ya no sirve de nada. Tenga cuidado fsync=offy full_page_writesaplique a nivel de clúster , de modo que afecten a todas las bases de datos en su instancia de PostgreSQL.

Para el uso de producción, posiblemente pueda usar synchronous_commit=offy establecer un commit_delay, ya que obtendrá muchos de los mismos beneficios que fsync=offsin el riesgo de corrupción de datos gigantes. Tiene una pequeña ventana de pérdida de datos recientes si habilita la confirmación asíncrona, pero eso es todo.

Si tiene la opción de alterar ligeramente el DDL, también puede usar UNLOGGEDtablas en la página 9.1+ para evitar completamente el registro de WAL y obtener un aumento de velocidad real a costa de que las tablas se borren si el servidor falla. No hay una opción de configuración para hacer que todas las tablas no se registren, se debe configurar durante CREATE TABLE. Además de ser bueno para las pruebas, esto es útil si tiene tablas llenas de datos generados o sin importancia en una base de datos que de lo contrario contiene cosas que necesita para estar seguro.

Verifique sus registros y vea si recibe advertencias sobre demasiados puntos de control. Si es así, debe aumentar sus checkpoint_segments . También es posible que desee ajustar su checkpoint_completion_target para suavizar las escrituras.

Sintonice shared_bufferspara adaptarse a su carga de trabajo. Esto depende del sistema operativo, depende de lo que esté sucediendo con su máquina y requiere algo de prueba y error. Los valores predeterminados son extremadamente conservadores. Es posible que deba aumentar el límite máximo de memoria compartida del sistema operativo si aumenta shared_buffersPostgreSQL 9.2 y versiones inferiores; 9.3 y superior cambió la forma en que usan la memoria compartida para evitar eso.

Si está utilizando solo un par de conexiones que hacen mucho trabajo, aumente work_mempara darles más RAM para jugar, etc. Tenga en cuenta que una work_memconfiguración demasiado alta puede causar problemas de falta de memoria porque es por tipo, no por conexión para que una consulta pueda tener muchos tipos anidados. Realmente solo tiene que aumentar work_memsi puede ver cómo se derraman tipos en el disco EXPLAINo si se registra con la log_temp_filesconfiguración (recomendado), pero un valor más alto también puede permitir que Pg elija planes más inteligentes.

Como dijo otro póster aquí, es aconsejable colocar el xlog y las tablas / índices principales en discos duros separados si es posible. Las particiones separadas son bastante inútiles, realmente quieres unidades separadas. Esta separación tiene mucho menos beneficio si está ejecutando fsync=offy casi ninguno si está utilizando UNLOGGEDtablas.

Finalmente, ajuste sus consultas. Asegúrese de que su random_page_costy seq_page_costrefleje el rendimiento de su sistema, asegúrese de que effective_cache_sizesea ​​correcto, etc. Utilícelo EXPLAIN (BUFFERS, ANALYZE)para examinar planes de consulta individuales y encienda el auto_explainmódulo para informar todas las consultas lentas. A menudo puede mejorar drásticamente el rendimiento de las consultas simplemente creando un índice apropiado o ajustando los parámetros de costo.

AFAIK no hay forma de configurar una base de datos completa o un clúster como UNLOGGED. Sería interesante poder hacerlo. Considere preguntar en la lista de correo de PostgreSQL.

Ajuste del sistema operativo del host

También puede hacer algunos ajustes a nivel del sistema operativo. Lo principal que puede querer hacer es convencer al sistema operativo de que no vacíe las escrituras en el disco de forma agresiva, ya que realmente no le importa cuándo / si llegan al disco.

En Linux se puede controlar esto con el subsistema de memoria virtual 's dirty_*ajustes, como dirty_writeback_centisecs.

El único problema con el ajuste de la configuración de reescritura es demasiado flojo es que una descarga de algún otro programa puede hacer que también se vacíen todos los búferes acumulados de PostgreSQL, causando grandes paradas mientras todo se bloquea en las escrituras. Es posible que pueda aliviar esto ejecutando PostgreSQL en un sistema de archivos diferente, pero algunas descargas pueden ser a nivel de dispositivo o a nivel de host completo, no a nivel de sistema de archivos, por lo que no puede confiar en eso.

Esta optimización realmente requiere jugar con la configuración para ver qué funciona mejor para su carga de trabajo.

En los núcleos más nuevos, es posible que desee asegurarse de que vm.zone_reclaim_modeesté configurado en cero, ya que puede causar graves problemas de rendimiento con los sistemas NUMA (la mayoría de los sistemas en estos días) debido a las interacciones con la gestión de PostgreSQL shared_buffers.

Consulta y ajuste de carga de trabajo

Estas son cosas que sí requieren cambios de código; Puede que no te convengan. Algunas son cosas que puede aplicar.

Si no está agrupando el trabajo en transacciones más grandes, comience. Muchas pequeñas transacciones son caras, por lo que debe agrupar las cosas siempre que sea posible y práctico. Si está utilizando la confirmación asincrónica, esto es menos importante, pero sigue siendo muy recomendable.

Siempre que sea posible, use tablas temporales. No generan tráfico WAL, por lo que son mucho más rápidos para inserciones y actualizaciones. A veces vale la pena deslizar un montón de datos en una tabla temporal, manipulándola como sea necesario y luego haciendo una INSERT INTO ... SELECT ...copia para copiarla en la tabla final. Tenga en cuenta que las tablas temporales son por sesión; si su sesión finaliza o pierde su conexión, la tabla temporal desaparece y ninguna otra conexión puede ver el contenido de las tablas temporales de una sesión.

Si está usando PostgreSQL 9.1 o más reciente, puede usar UNLOGGEDtablas para datos que puede permitirse perder, como el estado de la sesión. Estos son visibles en diferentes sesiones y se conservan entre las conexiones. Se truncan si el servidor se cierra de manera sucia, por lo que no se pueden usar para nada que no se pueda volver a crear, pero son excelentes para cachés, vistas materializadas, tablas de estado, etc.

En general, no lo hagas DELETE FROM blah;. Usar en su TRUNCATE TABLE blah;lugar; es mucho más rápido cuando estás volcando todas las filas en una tabla. Trunca muchas tablas en una TRUNCATEllamada si puedes. Sin TRUNCATESembargo, hay una advertencia si estás haciendo muchas mesas pequeñas una y otra vez; ver: velocidad de truncamiento Postgresql

Si no tiene índices en claves foráneas, los DELETEs que involucren las claves primarias a las que hacen referencia esas claves foráneas serán horriblemente lentos. Asegúrese de crear dichos índices si alguna vez lo espera DELETEde las tablas referenciadas. No se requieren índices para TRUNCATE.

No cree índices que no necesita. Cada índice tiene un costo de mantenimiento. Intente utilizar un conjunto mínimo de índices y deje que los escaneos de índice de mapa de bits los combinen en lugar de mantener demasiados índices enormes y costosos de múltiples columnas. Cuando se requieren índices, primero intente llenar la tabla y luego cree índices al final.

Hardware

Tener suficiente RAM para contener toda la base de datos es una gran victoria si puede administrarlo.

Si no tiene suficiente RAM, cuanto más rápido sea el almacenamiento, mejor. Incluso un SSD barato hace una gran diferencia sobre el óxido giratorio. Sin embargo, no confíe en los SSD baratos para la producción, a menudo no son seguros y pueden comer sus datos.

Aprendizaje

El libro de Greg Smith, PostgreSQL 9.0 High Performance sigue siendo relevante a pesar de referirse a una versión algo más antigua. Debería ser una referencia útil.

Únase a la lista de correo general de PostgreSQL y sígala.

Leyendo:

Craig Ringer
fuente
10
También puedo recomendar PostgreSQL 9.0 High Performance de @GregSmith, es realmente una gran lectura. El libro cubre todos los aspectos del ajuste del rendimiento, desde el diseño del disco hasta el ajuste de consultas, y le brinda una muy buena comprensión de los aspectos internos de PG.
tscho
10
No publiqué una actualización del libro para PostgreSQL 9.1, el único lanzamiento desde su publicación, porque no hubo suficientes cambios relacionados con el rendimiento en 9.1 para garantizarlo.
Greg Smith
3
Gran redacción. Solo como una pequeña actualización, "Es posible que deba aumentar el límite máximo de memoria compartida del sistema operativo si aumenta shared_buffers" ya no es cierto (para la mayoría de los usuarios) en PostgreSQL 9.3: postgresql.org/docs/9.3/static/release-9- 3.html # AEN114343
Gunnlaugur Briem
1
@brauliobo Mis pruebas a menudo hacen muchos tx en TPS alto ... porque trato de simular la producción, incluidas las cargas de trabajo pesadas de concurrencia. Si quiere decir "prueba lineal de conexión única", entonces estaría de acuerdo con usted.
Craig Ringer
1
stackoverflow.com/questions/11419536/… DELETE puede ser más rápido que TRUNCATE para tablas con pocas filas, lo que probablemente sea el caso en las pruebas.
Jonathan Crosmer
9

Utilice un diseño de disco diferente:

  • disco diferente por $ PGDATA
  • disco diferente para $ PGDATA / pg_xlog
  • disco diferente para archivos tem (por base de datos $ PGDATA / base // pgsql_tmp) (ver nota sobre work_mem)

ajustes de postgresql.conf:

  • shared_memory: 30% de la RAM disponible pero no más de 6 a 8GB. Parece mejor tener menos memoria compartida (2GB - 4GB) para cargas de trabajo intensivas de escritura
  • work_mem: principalmente para consultas seleccionadas con ordenaciones / agregaciones. Esto es por configuración de conexión y la consulta puede asignar ese valor varias veces. Si los datos no pueden caber, se usa el disco (pgsql_tmp). Marque "explicar analizar" para ver cuánta memoria necesita
  • fsync y synchronous_commit: los valores predeterminados son seguros, pero si puede tolerar la pérdida de datos, puede desactivarlos
  • random_page_cost: si tiene SSD o una matriz RAID rápida, puede reducir esto a 2.0 (RAID) o incluso más bajo (1.1) para SSD
  • checkpoint_segments: puede ir más alto 32 o 64 y cambiar checkpoint_completion_target a 0.9. Un valor más bajo permite una recuperación más rápida después del bloqueo
mi
fuente
44
Tenga en cuenta que si ya está ejecutando fsync=off, poner pg_xlog en un disco separado ya no mejora mucho.
intgr
El valor de 1.1 para SSD parece muy descalificado. Reconozco que es lo que algunos profesionales han recomendado ciegamente. Incluso los SSD son significativamente más rápidos para lecturas secuenciales que para lecturas aleatorias.
Acumenus
@ABB Sí, pero también tiene efectos de almacenamiento en caché del búfer del sistema operativo en el trabajo. Todos esos parámetros son un poco pesados ​​de todos modos ...
Craig Ringer