2012-02-28 10 views
8

estoy notando algo extraño/raro:PostgreSQL consulta no mediante el índice de la producción

La misma consulta exacta en el desarrollo/producción no están utilizando la misma ruta de consulta. En particular, la versión de desarrollo usa índices que se omiten en producción (a favor de seqscan).

La única diferencia real es que el conjunto de datos es la producción es significativamente mayor, el tamaño del índice es 1034 MB, frente a 29 MB en producción. ¿PostgreSQL se abstendría de usar índices si ellos (o la tabla) son demasiado grandes?

EDIT: EXPLAIN ANALYZE tanto para consultas:

Desarrollo:

Limit (cost=41638.15..41638.20 rows=20 width=154) (actual time=159.576..159.581 rows=20 loops=1) 
    -> Sort (cost=41638.15..41675.10 rows=14779 width=154) (actual time=159.575..159.577 rows=20 loops=1) 
     Sort Key: (sum(scenario_ad_group_performances.clicks)) 
     Sort Method: top-N heapsort Memory: 35kB 
     -> GroupAggregate (cost=0.00..41244.89 rows=14779 width=154) (actual time=0.040..151.535 rows=14197 loops=1) 
       -> Nested Loop Left Join (cost=0.00..31843.75 rows=93800 width=154) (actual time=0.022..82.509 rows=50059 loops=1) 
        -> Merge Left Join (cost=0.00..4203.46 rows=14779 width=118) (actual time=0.017..27.103 rows=14197 loops=1) 
          Merge Cond: (scenario_ad_groups.id = scenario_ad_group_vendor_instances.ad_group_id) 
          -> Index Scan using scenario_ad_groups_pkey on scenario_ad_groups (cost=0.00..2227.06 rows=14779 width=114) (actual time=0.009..12.085 rows=14197 loops=1) 
           Filter: (scenario_id = 22) 
          -> Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances (cost=0.00..1737.02 rows=27447 width=8) (actual time=0.007..7.021 rows=16528 loops=1) 
           Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[])) 
        -> Index Scan using index_ad_group_performances_on_vendor_instance_id_and_date on scenario_ad_group_performances (cost=0.00..1.73 rows=11 width=44) (actual time=0.002..0.003 rows=3 loops=14197) 
          Index Cond: ((vendor_instance_id = scenario_ad_group_vendor_instances.id) AND (date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date)) 
Total runtime: 159.710 ms 

Producción:

Limit (cost=822401.35..822401.40 rows=20 width=179) (actual time=21279.547..21279.591 rows=20 loops=1) 
    -> Sort (cost=822401.35..822488.42 rows=34828 width=179) (actual time=21279.543..21279.560 rows=20 loops=1) 
     Sort Key: (sum(scenario_ad_group_performances.clicks)) 
     Sort Method: top-N heapsort Memory: 33kB 
     -> GroupAggregate (cost=775502.60..821474.59 rows=34828 width=179) (actual time=19126.783..21226.772 rows=34495 loops=1) 
       -> Sort (cost=775502.60..776739.48 rows=494751 width=179) (actual time=19125.902..19884.164 rows=675253 loops=1) 
        Sort Key: scenario_ad_groups.id 
        Sort Method: external merge Disk: 94200kB 
        -> Hash Right Join (cost=25743.86..596796.70 rows=494751 width=179) (actual time=1155.491..16720.460 rows=675253 loops=1) 
          Hash Cond: (scenario_ad_group_performances.vendor_instance_id = scenario_ad_group_vendor_instances.id) 
          -> Seq Scan on scenario_ad_group_performances (cost=0.00..476354.29 rows=4158678 width=44) (actual time=0.043..8949.640 rows=4307019 loops=1) 
           Filter: ((date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date)) 
          -> Hash (cost=24047.72..24047.72 rows=51371 width=143) (actual time=1123.896..1123.896 rows=34495 loops=1) 
           Buckets: 1024 Batches: 16 Memory Usage: 392kB 
           -> Hash Right Join (cost=6625.90..24047.72 rows=51371 width=143) (actual time=92.257..1070.786 rows=34495 loops=1) 
             Hash Cond: (scenario_ad_group_vendor_instances.ad_group_id = scenario_ad_groups.id) 
             -> Seq Scan on scenario_ad_group_vendor_instances (cost=0.00..11336.31 rows=317174 width=8) (actual time=0.020..451.496 rows=431770 loops=1) 
              Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[])) 
             -> Hash (cost=5475.55..5475.55 rows=34828 width=139) (actual time=88.311..88.311 rows=34495 loops=1) 
              Buckets: 1024 Batches: 8 Memory Usage: 726kB 
              -> Bitmap Heap Scan on scenario_ad_groups (cost=798.20..5475.55 rows=34828 width=139) (actual time=4.451..44.065 rows=34495 loops=1) 
                Recheck Cond: (scenario_id = 276) 
                -> Bitmap Index Scan on index_scenario_ad_groups_on_scenario_id (cost=0.00..789.49 rows=34828 width=0) (actual time=4.232..4.232 rows=37006 loops=1) 
                 Index Cond: (scenario_id = 276) 
Total runtime: 21306.697 ms 
+0

El índice puede no ser lo suficientemente selectivo en su entorno de producción –

+0

¿Qué significa "no suficientemente selectivo"? – vonconrad

+1

Si tiene una columna booleana indexada donde el 99% de los datos es 'falso', no tiene mucho sentido escanear el índice y luego escanear el 99% de la tabla, en lugar de escanear toda la tabla. El índice no sería lo suficientemente selectivo para los valores 'false'. – aib

Respuesta

13

Negación

he utilizado PostgreSQL muy poco. Estoy respondiendo según mi conocimiento del uso del índice SQL Server y los planes de ejecución. Le pido misericordia a los dioses de PostgreSQL si obtengo algo mal.

optimizadores de consulta son dinámicos

Usted dijo que su plan de consulta ha cambiado desde su desarrollo a los entornos de producción. Esto es de esperar. Los optimizadores de consultas están diseñados para generar el plan de ejecución óptimo en función de las condiciones de datos actuales. Bajo diferentes condiciones, el optimizador puede decidir que es más eficiente usar una exploración de tabla que una exploración de índice.

¿Cuándo sería más eficiente utilizar una exploración de tabla frente a una exploración de índice?

SELECT A, B 
FROM someTable 
WHERE A = 'SOME VALUE' 

Digamos que usted tiene un índice no agrupado en la columna A. En este caso, está filtrando en la columna A, que podría aprovechar el índice. Esto sería eficiente si el índice es lo suficientemente selectivo; básicamente, ¿cuántos valores distintos componen el índice? La base de datos mantiene estadísticas sobre esta información de selectividad y utiliza estas estadísticas al calcular los costos de los planes de ejecución.

Si tiene un millón de filas en una tabla, pero solo 10 valores posibles para A, su consulta probablemente devolverá aproximadamente 100K filas. Como el índice no está agrupado y está devolviendo columnas no incluidas en el índice, B, se deberá realizar una búsqueda para cada fila devuelta. Estas búsquedas son búsquedas de acceso aleatorio que son mucho más caras que las lecturas secuenciales utilizadas por una exploración de tabla. En cierto punto, la base de datos se vuelve más eficiente para realizar una exploración de tabla en lugar de una exploración de índice.

Este es solo un caso, hay muchos otros. Es difícil saber sin saber más acerca de cómo son sus datos, cómo son sus índices y cómo está tratando de acceder a los datos.

Para responder a la pregunta original:

Would PostgreSQL se abstengan de utilizar índices si ellos (o la tabla) son demasiado grandes? No.Es más probable que en la forma en que está accediendo a los datos, para PostgreSQL sea menos eficiente usar el índice versus usar un escaneo de tabla.

El PostgreSQL Preguntas toca sobre este mismo tema (ver: ¿Por qué mis consultas lenta ¿Por qué no usan índices mis??): https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F optimizador de consultas

+0

La página wiki es genial. – Benoit

+0

El último enlace está roto - enlace de trabajo: https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F – dmikam

+0

Muy audaz comenzar una respuesta con ese tipo de exención de responsabilidad! – jwg2s

3

Postgres' viene con múltiples escenarios (por ejemplo, index vs seq-scan) y los evalúa usando información estadística sobre sus tablas y los costos relativos de acceso de disco/memoria/índice/tabla establecidos en la configuración.

¿Utilizaste el comando EXPLAIN para ver por qué se omitió el uso del índice? ¿Usaste EXPLAIN ANALYZE para averiguar si la decisión fue un error? ¿Podemos ver las salidas, por favor?

edición:

Tan difícil como el análisis de dos consultas singulares diferentes en diferentes sistemas son, me parece ver un par de cosas.

El entorno de producción tiene una tasa real/costo de alrededor de 20-100 milisegundos por unidad de costo. Ni siquiera soy un DBA, pero esto parece consistente. El entorno de desarrollo tiene 261 para la consulta principal. ¿Esto parece correcto? ¿Esperaría que la velocidad bruta (memoria/disco/CPU) del entorno de producción fuera de 2 a 10 veces más rápida que la de desarrollo?

Dado que el entorno de producción tiene un plan de consulta complejo mucho más, parece que está haciendo su trabajo. Sin lugar a dudas, el plan del entorno de desarrollo y muchos más se han considerado y se consideran demasiado costosos. Y la varianza de 20-100 no es mucho en mi experiencia (pero, nuevamente, no es un DBA) y muestra que no hay nada fuera de lugar. Aún así, es posible que desee ejecutar un VACUUM en la base de datos por si acaso.

No tengo experiencia y soy lo suficientemente paciente como para decodificar la consulta completa, pero ¿podría haber un punto de desnormalización/NOSQL-ización para la optimización?

El mayor cuello de botella parece ser la fusión del disco a 90 MB. Si el entorno de producción tiene suficiente memoria, es posible que desee aumentar la configuración relevante (¿memoria de trabajo?) Para hacerlo en la memoria. Parece ser el parámetro work_memhere, aunque querrá leer el resto.

También sugiero tener un look en el index usage statistics. Existen muchas opciones con índices parciales y funcionales.

+0

Edité mi pregunta con las salidas 'EXPLAIN ANALYZE'. – vonconrad

3

Trate

SET enable_seqscan TO 'off' 

antes EXPLAIN ANALYZE

+9

Tenga en cuenta que si explicó que esta es una técnica de solución de problemas para determinar si la consulta sería más rápida con un análisis de índice en lugar de una solución que se aplicará a ciegas a la base de datos, esta sería una respuesta mucho mejor. –

2

Me parece que sus datos dev es mucho "más simple" que los datos de producción. A modo de ejemplo:

Desarrollo:

-> Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances 
(cost=0.00..1737.02 rows=27447 width=8) 
(actual time=0.007..7.021 rows=16528 loops=1) 
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[])) 

Producción:

-> Seq Scan on scenario_ad_group_vendor_instances 
(cost=0.00..11336.31 rows=317174 width=8) 
(actual time=0.020..451.496 rows=431770 loops=1) 
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[])) 

Esto significa, que la fila coincidente en dev 27447 se han estimado por adelantado y 16528 filas se encuentran de hecho. Ese es el mismo estadio y está bien.

En producción 317174 filas coincidentes se han estimado por adelantado y se encontraron 431770 filas. También está bien.

Pero la comparación de dev to prod significa que los números son 10 veces diferentes. Como ya indican otras respuestas, hacer 10 veces más búsquedas aleatorias (debido al acceso al índice) podría ser peor que una exploración de tabla simple.

Por lo tanto, la pregunta interesante es: ¿Cuántas filas contiene esta tabla tanto en dev como en prod? ¿Es number_returned_rows/number_total_rows comparable entre dev y prod?

Editar No se olvide: He recogido una acceso índice como un ejemplo. Un vistazo rápido muestra que los otros accesos de índice tienen los mismos síntomas.

Cuestiones relacionadas