¿Cómo obligo a Postgres a usar un índice en particular?


112

¿Cómo obligo a Postgres a usar un índice cuando, de lo contrario, insistiría en hacer un escaneo secuencial?



1
+1 Me encantaría ver esta función. No se trata simplemente de deshabilitar el escaneo seq, como dicen otras respuestas: necesitamos la capacidad de obligar a PG a usar un índice específico . Esto se debe a que, en la palabra real, las estadísticas pueden ser completamente incorrectas y, en ese momento, debe usar soluciones parciales / poco confiables. Estoy de acuerdo en que, en casos simples, primero debe verificar los índices y otras configuraciones, pero para confiabilidad y usos avanzados en big data, necesitamos esto.
collimarco

MySQL y Oracle lo tienen ... No estoy seguro de por qué el planificador de Postgres es tan poco confiable.
Kevin Parker

Respuestas:


103

Suponiendo que está preguntando acerca de la característica común de "sugerencias de índice" que se encuentra en muchas bases de datos, PostgreSQL no proporciona dicha característica. Esta fue una decisión consciente tomada por el equipo de PostgreSQL. Puede encontrar una buena descripción general de por qué y qué puede hacer en su lugar aquí . Las razones son básicamente que es un truco de rendimiento que tiende a causar más problemas en el futuro a medida que cambian sus datos, mientras que el optimizador de PostgreSQL puede reevaluar el plan en función de las estadísticas. En otras palabras, lo que hoy podría ser un buen plan de consulta probablemente no será un buen plan de consulta para siempre, y las sugerencias de índice fuerzan un plan de consulta particular para siempre.

Como martillo muy contundente, útil para realizar pruebas, puede utilizar los parámetros enable_seqscany enable_indexscan. Ver:

Estos no son adecuados para un uso de producción continuo . Si tiene problemas con la elección del plan de consultas, debe consultar la documentación para rastrear los problemas de rendimiento de las consultas . No se limite a establecer enable_parámetros y alejarse.

A menos que tenga una muy buena razón para usar el índice, es posible que Postgres esté tomando la decisión correcta. ¿Por qué?

  • Para tablas pequeñas, es más rápido hacer exploraciones secuenciales.
  • Postgres no usa índices cuando los tipos de datos no coinciden correctamente, es posible que deba incluir las conversiones adecuadas.
  • La configuración de su planificador puede estar causando problemas.

Vea también esta antigua publicación del grupo de noticias .


4
De acuerdo, obligar a Postgres a hacerlo a tu manera generalmente significa que lo has hecho mal. 9/10 veces el planificador superará cualquier cosa que se le ocurra. La otra 1 vez es porque lo hiciste mal.
Kent Fredric

Creo que es una buena idea verificar realmente las clases de operadores de su índice de retención.
metdos

2
Odio revivir una vieja pregunta, pero veo a menudo en la documentación de Postgres, discusiones y aquí, pero ¿hay un concepto generalizado de lo que califica para una mesa pequeña ? ¿Es algo así como 5000 filas o 50000, etc.?
waffl

1
@waffl ¿Ha considerado la evaluación comparativa? Cree una tabla simple con un índice y una función adjunta para llenarla con n filas de basura aleatoria. Luego, comience a buscar en el plan de consulta diferentes valores de n . Cuando vea que comienza a usar el índice, debería tener una respuesta aproximada. También puede obtener escaneos secuenciales si PostgreSQL determina (según las estadísticas) que un escaneo de índice no eliminará muchas filas también. Por lo tanto, la evaluación comparativa siempre es una buena idea cuando tiene preocupaciones reales sobre el rendimiento. Como una suposición anecdótica e improvisada, yo diría que un par de miles suele ser "pequeña".
jpmc26

10
Con más de 30 años de experiencia en plataformas como Oracle, Teradata y MSSQL, encuentro que el optimizador de PostgreSQL 10 no es especialmente inteligente. Incluso con estadísticas actualizadas genera planes de ejecución menos eficientes que los forzados en una dirección especial. Proporcionar sugerencias estructurales para compensar estos problemas proporcionaría una solución que permitiría a PostgreSQL crecer en más segmentos del mercado. EN MI HUMILDE OPINIÓN.
Guido Leenders

75

Probablemente la única razón válida para usar

set enable_seqscan=false

es cuando está escribiendo consultas y desea ver rápidamente cuál sería realmente el plan de consulta si hubiera grandes cantidades de datos en la (s) tabla (s). O, por supuesto, si necesita confirmar rápidamente que su consulta no utiliza un índice simplemente porque el conjunto de datos es demasiado pequeño.


41
esta breve respuesta en realidad da una buena pista para propósitos de prueba
dwery

3
¡Nadie está respondiendo a la pregunta!
Ivailo Bardarov

@IvailoBardarov La razón por la que todas estas otras sugerencias están aquí es porque PostgreSQL no tiene esta característica; esta fue una decisión consciente tomada por los desarrolladores basada en cómo se usa típicamente y los problemas a largo plazo que causa.
jpmc26

Un buen truco para probar: ejecutar set enable_seqscan=false, ejecutar su consulta y luego ejecutar rápidamente set enable_seqscan=truepara devolver postgresql a su comportamiento correcto (y obviamente no haga esto en producción, ¡solo en desarrollo!)
Brian Hellekin

2
@BrianHellekin Mejor, SET SESSION enable_seqscan=falsesolo
afectarte

20

A veces, PostgreSQL no puede hacer la mejor elección de índices para una condición particular. Como ejemplo, suponga que hay una tabla de transacciones con varios millones de filas, de las cuales hay varios cientos para un día determinado, y la tabla tiene cuatro índices: transaction_id, client_id, date y description. Quiere ejecutar la siguiente consulta:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL puede optar por utilizar el índice transaction_description_idx en lugar de transaction_date_idx, lo que puede llevar a que la consulta tarde varios minutos en lugar de menos de un segundo. Si este es el caso, puede forzar el uso del índice en la fecha modificando la condición de esta manera:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id

3
Buena idea. Sin embargo, cuando deshabilitamos el uso actual del índice con este método, el optimizador de consultas de postgresql retrocede al siguiente índice adecuado. Por lo tanto, no hay garantía de que el optimizador elija your_wanted_index, puede ser que el motor postgresql solo realice un escaneo de secuencia / clave primaria en su lugar. Conclusión: no existe un método 100% confiable para forzar el uso de algún índice para el servidor PostgreSql.
Agnius Vasiliauskas

¿Qué pasa si no hay ninguna wherecondición más que dos tablas o unidas y Postgres no toma el índice?
Luna Lovegood

@Surya lo anterior se aplica tanto a WHERE como a las condiciones de JOIN ... ON
Ziggy Crueltyfree Zeitgeister

18

Respuesta corta

Este problema suele ocurrir cuando el costo estimado de un escaneo de índice es demasiado alto y no refleja correctamente la realidad. Es posible que deba reducir el random_page_costparámetro de configuración para solucionar este problema. De la documentación de Postgres :

Reducir este valor hará que el [...] sistema prefiera los escaneos de índice; aumentarlo hará que las exploraciones de índices parezcan relativamente más caras.

Puede verificar si un valor más bajo realmente hará que Postgres use el índice (pero use esto solo para pruebas ):

EXPLAIN <query>;              # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>;              # May use index scan now

Puede restaurar el valor predeterminado con SET random_page_cost = DEFAULT;nuevamente.

Antecedentes

Las exploraciones de índice requieren recuperaciones de páginas de disco no secuenciales. Postgres utiliza random_page_costpara estimar el costo de tales recuperaciones no secuenciales en relación con las recuperaciones secuenciales. El valor predeterminado es 4.0, asumiendo un factor de costo promedio de 4 en comparación con las recuperaciones secuenciales (teniendo en cuenta los efectos del almacenamiento en caché).

Sin embargo, el problema es que este valor predeterminado no es adecuado en los siguientes escenarios importantes de la vida real:

1) Unidades de estado sólido

Como admite la documentación:

El almacenamiento que tiene un costo de lectura aleatorio bajo en relación con el secuencial, por ejemplo, unidades de estado sólido, podría modelarse mejor con un valor más bajo para random_page_cost.

De acuerdo con el último punto de esta diapositiva de una charla en la PostgresConf 2018, random_page_costdebería establecerse en algo intermedio 1.0y 2.0para unidades de estado sólido.

2) datos en caché

Si los datos de índice requeridos ya están almacenados en caché en la RAM, un escaneo de índice siempre será significativamente más rápido que un escaneo secuencial. La documentación dice:

En consecuencia, si es probable que sus datos estén completamente en la caché, [...] random_page_costpuede ser apropiado disminuirlos .

El problema es que, por supuesto, no puede saber fácilmente si los datos relevantes ya están almacenados en caché. Sin embargo, si se consulta con frecuencia un índice específico, y si el sistema tiene suficiente RAM, es probable que los datos se almacenen en caché y se random_page_costdeben establecer en un valor más bajo. Tendrá que experimentar con diferentes valores y ver qué funciona para usted.

Es posible que también desee utilizar la extensión pg_prewarm para el almacenamiento en caché de datos explícitos.



2
Incluso tuve que establecer random_page_cost = 0.1 para que el escaneo de índices funcione en una tabla grande (~ 600M filas) en Pg 10.1 en Ubuntu. Sin el ajuste, el escaneo de secuencia (a pesar de ser paralelo) tardaba 12 minutos (¡Tenga en cuenta que se realizó el análisis de la tabla!). La unidad es SSD. Después del ajuste, el tiempo de ejecución se convirtió en 1 segundo.
Anatoly Alekseev

Salvaste mi día. Me estaba volviendo loco tratando de averiguar cómo exactamente la misma consulta en la misma base de datos tardaba 30 segundos en una máquina y menos de 1 en otra, incluso después de ejecutar el análisis en ambos extremos ... A quién le puede interesar: el comando ' ALTER SYSTEM SET random_page_cost = x 'establece el nuevo valor predeterminado globalmente.
Julien

10

La pregunta en sí misma es muy inválida. Forzar (haciendo enable_seqscan = off, por ejemplo) es una muy mala idea. Puede ser útil comprobar si será más rápido, pero el código de producción nunca debería utilizar tales trucos.

En su lugar, explique el análisis de su consulta, léala y descubra por qué PostgreSQL elige un plan incorrecto (en su opinión).

Hay herramientas en la web que ayudan con la lectura, explican, analizan la salida, una de ellas es explica.depesz.com , escrita por mí.

Otra opción es unirse al canal #postgresql en la red freenode irc y hablar con los chicos que están allí para ayudarlo, ya que optimizar la consulta no es una cuestión de "hacer una pregunta, obtener una respuesta, ser feliz". es más como una conversación, con muchas cosas que comprobar, muchas cosas que aprender.


2

Hay un truco para presionar postgres para preferir un seqscan agregando un OFFSET 0en la subconsulta

Esto es útil para optimizar solicitudes que vinculan tablas grandes / enormes cuando todo lo que necesita son solo los n primeros / últimos elementos.

Supongamos que está buscando los primeros / últimos 20 elementos que involucren varias tablas que tengan 100k (o más) entradas, no tiene sentido construir / vincular toda la consulta sobre todos los datos cuando lo que buscará está en los primeros 100 o 1000 entradas. En este escenario, por ejemplo, resulta 10 veces más rápido hacer un escaneo secuencial.

consulte ¿Cómo puedo evitar que Postgres incluya una subconsulta?


Buen truco. Aunque un buen optimizador debería, por supuesto, optimizar la compensación 0 :-)
Guido Leenders
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.