sql >> Base de Datos >  >> RDS >> PostgreSQL

Insinuando a PostgreSQL

La guerra de llamas de esta semana en la lista de rendimiento de pgsql una vez más gira en torno al hecho de que PostgreSQL no tiene la sintaxis de sugerencias tradicional disponible en otras bases de datos. Hay una combinación de razones técnicas y pragmáticas detrás de esto:

  • Introducir sugerencias es una fuente común de problemas posteriores, porque corregir un lugar de consulta una vez en un caso especial no es un enfoque muy sólido. A medida que su conjunto de datos crece, y posiblemente también cambie la distribución, la idea que insinuó cuando era pequeña puede convertirse en una idea cada vez más mala.
  • Agregar una interfaz de sugerencias útil complicaría el código del optimizador, que es bastante difícil de mantener tal como está. Parte de la razón por la que PostgreSQL funciona tan bien como lo hace con la ejecución de consultas es porque el código se siente bien ("¡podemos marcar las sugerencias en nuestra lista de características de comparación de proveedores!") que en realidad no se paga por sí mismo, en términos de hacer que la base de datos lo suficientemente mejor como para justificar su mantenimiento continuado, es rechazado por la política. Si no funciona, no se agregará. Y cuando se evalúan objetivamente, las sugerencias son, en promedio, un problema en lugar de una solución.
  • El tipo de problemas que sugieren que funciona pueden ser errores del optimizador. La comunidad de PostgreSQL responde a errores reales en el optimizador más rápido que nadie en la industria. Pregunte y no tendrá que conocer a muchos usuarios de PostgreSQL antes de encontrar a uno que haya informado un error y lo haya solucionado al día siguiente.

Ahora, la principal respuesta completamente válida para descubrir que faltan sugerencias, normalmente de los administradores de bases de datos que están acostumbrados a ellas, es "bueno, ¿cómo manejo un error del optimizador cuando me encuentro con él?" Como todo trabajo tecnológico hoy en día, generalmente hay una gran presión para obtener la solución más rápida posible cuando surge un problema de consulta incorrecta.
Si PostgreSQL no tuviera algunas formas de lidiar con esa situación, no habría bases de datos PostgreSQL de producción serias. . La diferencia es que las cosas que ajusta en esta base de datos están más enraizadas en influir en las decisiones que el optimizador ya toma de una manera bastante sutil, en lugar de simplemente decirle qué hacer. Estas son sugerencias en el sentido literal de la palabra, simplemente no tienen la interfaz de usuario para sugerir que los usuarios de otras bases de datos nuevos en PostgreSQL buscan.
Con eso en mente, echemos un vistazo a lo que puede hacer en PostgreSQL para solucionar los planes de consulta incorrectos y los errores del optimizador, en particular las cosas que muchas personas parecen pensar que solo se pueden resolver con sugerencias:

  • join_collapse_limit: Esto ajusta la flexibilidad que tiene el optimizador para reordenar las uniones de varias tablas. Normalmente, prueba todas las combinaciones posibles cuando las uniones se pueden reorganizar (que es la mayoría de las veces, a menos que esté usando una combinación externa). Reducir join_collapse_limit, quizás incluso a 1, elimina parte o toda esta flexibilidad. Con el valor 1, obtendrá las uniones en el orden en que las escribió, punto. La planificación de un gran número de uniones es una de las cosas más difíciles de hacer para el optimizador; cada combinación magnifica los errores en las estimaciones y aumenta el tiempo de planificación de consultas. Si la naturaleza subyacente de sus datos hace que sea obvio qué orden deben ocurrir las uniones, y no espera que eso cambie nunca, una vez que descubra el orden correcto, puede bloquearlo usando este parámetro.
  • random_page_cost:con un valor predeterminado de 4,0, este parámetro establece el coste de buscar en el disco para encontrar una página aleatoria en el disco, en relación con un valor de referencia de 1,0. Ahora, en realidad, si mide la proporción de E/S aleatoria a secuencial en discos duros normales, encontrará que este número está más cerca de 50. Entonces, ¿por qué 4.0? Primero, porque funcionó mejor que los valores más grandes en las pruebas comunitarias. En segundo lugar, en muchos casos, los datos de índice en particular se almacenarán en caché en la memoria, lo que reducirá el costo efectivo de leer esos valores. Si, por ejemplo, su índice está almacenado en caché en un 90 % en RAM, eso significa que el 10 % del tiempo realizará la operación que es 50 veces más costosa; eso haría que su costo de página aleatorio efectivo sea de aproximadamente 5. Este tipo de situación del mundo real es la razón por la cual el valor predeterminado tiene sentido donde está. Normalmente veo que los índices populares obtienen> 95% de caché en la memoria. Si es mucho más probable que su índice esté en RAM, reducir el costo de la página aleatoria hasta justo por encima de 1.0 puede ser una opción razonable, para reflejar que no es más costoso que cualquier otra lectura. Al mismo tiempo, las búsquedas aleatorias en sistemas realmente ocupados pueden ser mucho más costosas que las expectativas que tendría al observar simulaciones de un solo usuario. Tuve que configurar random_page_cost tan alto como 60 para que la base de datos dejara de usar índices cuando el planificador estaba calculando mal lo caros que serían. Por lo general, esa situación proviene de un error de estimación de sensibilidad por parte del planificador:si está escaneando más del 20% de una tabla, el planificador sabe que usar un Escaneo secuencial será mucho más eficiente que un Escaneo de índice. La desagradable situación en la que tuve que forzar ese comportamiento mucho antes se produjo cuando el planificador esperaba que se devolviera el 1 % de las filas, pero en realidad estaba más cerca del 15 %.
  • work_mem: ajusta la cantidad de memoria disponible para las consultas que realizan operaciones de clasificación, hashing y operaciones similares basadas en la memoria. Esta es solo una guía aproximada para las consultas, no un límite estricto, y un solo cliente puede terminar usando múltiplos de work_mem cuando ejecuta una consulta. En consecuencia, debe tener cuidado de no establecer este valor demasiado alto en el archivo postgresql.conf. Sin embargo, lo que puede hacer en su lugar es configurarlo antes de ejecutar una consulta que realmente se beneficia de tener memoria adicional para almacenar datos de clasificación o hash. A veces puede encontrar estas consultas al registrar las lentas usando log_min_duration_statement. También puede encontrarlos activando log_temp_files, que registrará cada vez que work_mem sea demasiado pequeño y, por lo tanto, las operaciones de clasificación se derramen en el disco en lugar de ocurrir en la memoria.
  • OFFSET 0:PostgreSQL reorganizará las subconsultas en forma de combinación, por lo que luego puede usar la lógica de orden de combinación normal para optimizarlas. En algunos casos, esa decisión puede ser realmente mala, ya que el tipo de cosas que las personas tienden a escribir como subconsultas parecen un poco más difíciles de estimar por alguna razón (lo digo en función de la cantidad de consultas problemáticas que veo). Un truco furtivo que puede hacer para evitar esta lógica es poner OFFSET 0 al final de la subconsulta. Esto no cambia los resultados en absoluto, pero insertar el tipo de nodo de consulta Limit utilizado para ejecutar OFFSET evitará la reorganización. La subconsulta siempre se ejecutará de la manera que la mayoría de la gente espera, como su propio nodo de consulta aislado.
  • enable_seqscan, enable_indexscan, enable_bitmapscan:  Desactivar una de estas funciones para buscar filas en una tabla es un martillo bastante grande para recomendar encarecidamente evitar ese tipo de escaneo (no siempre prevenirlo, si no hay forma de ejecutar su plan pero un seqscan, obtendrá un seqscan incluso si los parámetros están desactivados). Lo principal para lo que los recomiendo no es para solucionar consultas, sino para experimentar con EXPLAIN y ver por qué se prefirió el otro tipo de análisis.
  • enable_nestloop, enable_hashjoin, enable_mergejoin:si sospecha que su problema es el tipo de combinación que se usa en lugar de cómo se leen las tablas, intente desactivar el tipo que está viendo en su plan usando uno de estos parámetros, luego ejecute EXPLAIN otra vez. Los errores en las estimaciones de sensibilidad pueden hacer que una combinación parezca más o menos eficiente de lo que realmente es. Y, nuevamente, ver cómo cambia el plan con el método de unión actual deshabilitado puede ser muy informativo sobre por qué se decidió por ese en primer lugar.
  • enable_hashagg, enable_material:estas funciones son relativamente nuevas en PostgreSQL. El uso agresivo de Hash Aggregation se introdujo en la versión 8.4 y una materialización más agresiva en la 9.0. Si ve esos tipos de nodos en su salida EXPLAIN
    y parecen estar haciendo algo mal, debido a que este código es mucho más nuevo, es un poco más probable que tenga una limitación o un error que algunas de las características anteriores. Si tenía un plan que funcionaba bien en versiones anteriores de PostgreSQL, pero usa uno de estos tipos de nodos y, como resultado, parece funcionar mucho peor, deshabilitar estas funciones a veces puede devolverlo al comportamiento anterior, así como arrojar algo de luz sobre por qué el optimizador hizo lo incorrecto como retroalimentación útil. Tenga en cuenta que, por lo general, esta es la forma en que las características más avanzadas tienden a introducirse en PostgreSQL:con la opción de desactivarlo para solucionar problemas, si se demuestra que hay una regresión del plan en relación con la forma en que las versiones anteriores ejecutaron las cosas.
  • cursor_tuple_fraction: si no tiene la intención de leer todas las filas de una consulta, debe usar un cursor para implementar eso. En ese caso, el optimizador intenta priorizar si le devuelve la primera fila rápidamente o si prefiere optimizar toda la consulta, en función de este parámetro. De manera predeterminada, la base de datos asume que volverá a leer el 10% de la consulta cuando use un cursor. Ajustar este parámetro le permite sesgarlo para esperar que lea menos o más que eso.

Todos estos parámetros y ajustes de consulta deben considerarse ajustes de clasificación. No desea ejecutar estos en su lugar para siempre (excepto quizás para join_collapse_limit). Los usa para salir de un aprieto y luego, con suerte, descubrirá cuál es la verdadera causa subyacente del mal plan (malas estadísticas, limitación/error del optimizador o algo más) y luego abordará el problema desde esa dirección. Cuanto más empuje el comportamiento del optimizador en una dirección, más expuesto estará a cambios futuros en sus datos, lo que hará que ese empuje ya no sea el correcto. Si los usa correctamente, como una forma de estudiar por qué obtuvo el plan equivocado (el enfoque que usé en el capítulo de optimización de consultas de PostgreSQL 9.0 High Performance), la forma en que insinúa las cosas en PostgreSQL debería hacer que abandone cada ejecución. con un mal comportamiento del optimizador un poco más inteligente sobre cómo evitar ese tipo de problema en el futuro