¿Cómo obligo a Postgres a usar un índice cuando, de lo contrario, insistiría en hacer un escaneo secuencial?
¿Cómo obligo a Postgres a usar un índice cuando, de lo contrario, insistiría en hacer un escaneo secuencial?
Respuestas:
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_seqscan
y 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é?
Vea también esta antigua publicación del grupo de noticias .
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.
set enable_seqscan=false
, ejecutar su consulta y luego ejecutar rápidamente set enable_seqscan=true
para devolver postgresql a su comportamiento correcto (y obviamente no haga esto en producción, ¡solo en desarrollo!)
SET SESSION enable_seqscan=false
solo
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
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.
where
condición más que dos tablas o unidas y Postgres no toma el índice?
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_cost
pará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.
Las exploraciones de índice requieren recuperaciones de páginas de disco no secuenciales. Postgres utiliza random_page_cost
para 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_cost
debería establecerse en algo intermedio 1.0
y 2.0
para 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_cost
puede 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_cost
deben 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.
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.
Hay un truco para presionar postgres para preferir un seqscan agregando un OFFSET 0
en 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?