Pasé más de una hora preguntándome sobre un plan de consulta que no podía entender. La consulta era UDPATE
y simplemente no se ejecutaría TOTALMENTE. Totalmente estancado: pg_locks
mostró que tampoco esperaba nada. Ahora, no me considero el mejor o el peor tipo de lector de plan de consulta, pero me parece excepcionalmente difícil. Me pregunto ¿cómo se lee esto? ¿Existe alguna metodología que los Pg aces sigan para identificar el error?¿Cómo puedo "pensar mejor" cuando leo un plan de consulta PostgreSQL? (Ejemplo adjunto)
Planeo hacerme otra pregunta sobre cómo solucionar este problema, pero en este momento estoy hablando específicamente en cómo leer este tipo de planes. Por favor, no apunte a ningún tutorial genérico a menos que aborde específicamente este problema, resaltado debajo del plan de consulta.
QUERY PLAN
--------------------------------------------------------------------------------------------
Nested Loop Anti Join (cost=47680.88..169413.12 rows=1 width=77)
Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name))
-> Nested Loop (cost=5301.58..31738.10 rows=1 width=81)
-> Hash Join (cost=5301.58..29722.32 rows=229 width=40)
Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
-> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36)
Filter: (name IS NULL)
-> Hash (cost=4547.33..4547.33 rows=36150 width=24)
-> Seq Scan on vehicles iv (cost=0.00..4547.33 rows=36150 width=24)
Filter: (date_sold IS NULL)
-> Index Scan using options_pkey on options co (cost=0.00..8.79 rows=1 width=49)
Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code))
-> Hash Join (cost=42379.30..137424.09 rows=16729 width=26)
Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text))
-> Seq Scan on vehicles v (cost=0.00..4547.33 rows=65233 width=24)
-> Hash (cost=20223.32..20223.32 rows=931332 width=44)
-> Seq Scan on options o (cost=0.00..20223.32 rows=931332 width=44)
(17 rows)
El problema con este plan de consulta - Creo que entiendo - es probablemente mejor dicho por RhodiumToad
(que es definitivamente mejor en esto, así que voy a apostar por su explicación ser mejor) de irc://irc.freenode.net/#postgresql
:
oh, ese plan es potencialmente desastroso el problema con ese plan es que se está ejecutando un hashjoin muy costoso para cada fila el problema es la filas = 1 estimación de la otra se unen y el planificador piensa que está bien pon una gran una consulta costosa en la ruta interna de un nestloop donde se estima que el camino externo devuelve solo una fila. dado que, obviamente, según la estimación del planificador, la parte costosa solo se ejecutará una vez pero esto tiene una tendencia obvia a realmente arruinar en la práctica el problema es que el planificador cree que su propia estimación idealmente, el planificador necesita conocer la diferencia entre "estima para volver 1 fila" y "no es posible devolver más de 1 fila" pero no es del todo claro cómo incorporar eso en el código existente
Él va a decir:
puede afectar cualquier unión, pero generalmente se une subconsultas son los más propensos
Ahora cuando leo este plan la primera cosa que noté fue el Nested Loop Anti Join
, esto tuvo un costo de 169,413
(me quedo con límites superiores). Este Anti-Join se descompone en el resultado de Nested Loop
al costo de 31,738
, y el resultado de Hash Join
a un costo de 137,424
. Ahora, el 137,424
, es mucho mayor que 31,738
, así que sabía que el problema era el Hash Join. Luego procedo al EXPLAIN ANALYZE
el segmento Hash Join fuera de la consulta. Se ejecutó en 7 segundos. Me aseguré de que hubiera índices en (lot_id, vin), y (co.code y v.code) - había. Inhabilité seq_scan
y hashjoin
individualmente y noté un aumento de velocidad de menos de 2 segundos. No lo suficientemente cerca como para dar cuenta de por qué no estaba progresando después de una hora.
¡Pero, después de todo esto, estoy totalmente equivocado! Sí, era la parte más lenta de la consulta, pero debido al bit rows="1"
(supongo que estaba en el Nested Loop Anti Join
). ¿Hay algún tutorial que me ayude a identificar este tipo de problemas? Aquí hay un error (falta de habilidad) en el planificador al estimar erróneamente la cantidad de filas.¿Cómo se supone que debo leer esto para llegar a la misma conclusión que RhodiumToad
?
¿Es simplemente rows="1"
que se supone que me desencadenó a resolver esto?
Ejecuté VACUUM FULL ANALYZE
en todas las tablas involucradas, y esto es Postgresql 8.4.
Esta es una gran respuesta, pero ¿de qué estás hablando cuando dices 'join in co'? Creo que la explicación de 'RhodiumToads' del problema porque suena exacto? ¿Estás explicando lo mismo o algo diferente? –
Lo mismo. Unirse en 'co' es el' Index Scan usando options_pkey en las opciones co' del nodo interno de la unión de bucle anidado. Tiene dos condiciones que, probablemente, el planificador, de forma poco realista, dará como resultado una fila de producción.Si intenta ejecutar esa consulta y ver cuántas filas realmente devuelve, puede verificar si este es el caso. Las estimaciones incorrectas para los predicados correlacionados son un problema conocido. Hay un debate sobre esto en la lista de rendimiento: http://archives.postgresql.org/pgsql-performance/2009-06/msg00055.php –