Respuestas:
Explicar_EXPLAIN.pdf también podría ayudar.
La parte que siempre encontré confusa es el costo inicial frente al costo total. Busco esto en Google cada vez que lo olvido, lo que me trae de vuelta a aquí, lo que no explica la diferencia, por eso estoy escribiendo esta respuesta. Esto es lo que he obtenido de la documentación de PostgresEXPLAIN
, explicado como lo entiendo.
Aquí hay un ejemplo de una aplicación que administra un foro:
EXPLAIN SELECT * FROM post LIMIT 50;
Limit (cost=0.00..3.39 rows=50 width=422)
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Aquí está la explicación gráfica de PgAdmin:
(Cuando usa PgAdmin, puede apuntar con el mouse a un componente para leer los detalles del costo).
El costo se representa como una tupla, por ejemplo, el costo del LIMIT
es cost=0.00..3.39
y el costo del escaneo secuencial del post
es cost=0.00..15629.12
. El primer número de la tupla es el costo inicial y el segundo número es el costo total . Debido a que usé EXPLAIN
y no EXPLAIN ANALYZE
, estos costos son estimaciones, no medidas reales.
Como complicación, los costos de cada nodo "principal" incluyen los costos de sus nodos secundarios. En la representación de texto, el árbol se representa mediante sangría, por ejemplo, LIMIT
es un nodo padre y Seq Scan
es su hijo. En la representación de PgAdmin, las flechas apuntan de hijo a padre, la dirección del flujo de datos, lo que podría ser contrario a la intuición si está familiarizado con la teoría de grafos.
La documentación dice que los costos incluyen todos los nodos secundarios, pero observe que el costo total del padre 3.39
es mucho menor que el costo total de su hijo 15629.12
. El costo total no es inclusivo porque un componente como LIMIT
no necesita procesar toda su entrada. Vea el EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
ejemplo en la documentación de PostgresEXPLAIN
.
En el ejemplo anterior, el tiempo de inicio es cero para ambos componentes, porque ninguno de los componentes necesita realizar ningún procesamiento antes de comenzar a escribir filas: una exploración secuencial lee la primera fila de la tabla y la emite. El LIMIT
lee la primera fila y luego la emite.
¿Cuándo necesitaría un componente realizar mucho procesamiento antes de que pueda comenzar a generar filas? Hay muchas razones posibles, pero veamos un ejemplo claro. Aquí está la misma consulta de antes pero ahora contiene una ORDER BY
cláusula:
EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;
Limit (cost=23283.24..23283.37 rows=50 width=422)
-> Sort (cost=23283.24..23859.27 rows=230412 width=422)
Sort Key: body
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Y gráficamente:
Una vez más, el escaneo secuencial post
no tiene costo de inicio: comienza a generar filas de inmediato. Pero la ordenación tiene un costo inicial significativo 23283.24
porque tiene que ordenar toda la tabla antes de que pueda generar incluso una sola fila . El costo total de la clasificación 23859.27
es solo un poco más alto que el costo inicial, lo que refleja el hecho de que una vez que se ha ordenado todo el conjunto de datos, los datos clasificados se pueden emitir muy rápidamente.
Observe que el tiempo de inicio del LIMIT
23283.24
es exactamente igual al tiempo de inicio del tipo. Esto no se debe LIMIT
a que tenga un tiempo de inicio elevado. En realidad, tiene un tiempo de inicio cero por sí solo, pero EXPLAIN
suma todos los costos secundarios para cada padre, por lo que el LIMIT
tiempo de inicio incluye la suma de los tiempos de inicio de sus hijos.
Esta acumulación de costos puede dificultar la comprensión del costo de ejecución de cada componente individual. Por ejemplo, nuestro LIMIT
tiene un tiempo de inicio cero, pero eso no es obvio a primera vista. Por esta razón, varias otras personas se vincularon aexplica.depesz.com , una herramienta creada por Hubert Lubaczewski (también conocido como depesz) que ayuda a comprender EXPLAIN
, entre otras cosas, restando los costos de los hijos de los costos de los padres. Menciona algunas otras complejidades en una breve publicación de blog sobre su herramienta.
Se ejecuta desde el más sangrado al menos sangrado, y creo que desde la parte inferior del plan hasta la parte superior. (Entonces, si hay dos secciones sangradas, la que está más abajo en la página se ejecuta primero, luego, cuando se encuentran, la otra se ejecuta y luego se ejecuta la regla que las une).
La idea es que en cada paso haya 1 o 2 conjuntos de datos que lleguen y sean procesados por alguna regla. Si solo hay un conjunto de datos, esa operación se realiza en ese conjunto de datos. (Por ejemplo, escanee un índice para averiguar qué filas desea, filtre un conjunto de datos u ordene). Si hay dos, los dos conjuntos de datos son las dos cosas que tienen más sangría y están unidas por la regla que ve. El significado de la mayoría de las reglas se puede adivinar con bastante facilidad (especialmente si ha leído un montón de planes explicativos antes), sin embargo, puede intentar verificar elementos individuales ya sea mirando en la documentación o (más fácil) simplemente lanzando la frase en Google junto con algunas palabras clave como EXPLAIN
.
Obviamente, esta no es una explicación completa, pero proporciona suficiente contexto para que normalmente pueda averiguar lo que quiera. Por ejemplo, considere este plan de una base de datos real:
explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';
------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge Join (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
Merge Cond: (a.orderid = b.orderid)
-> Sort (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
Sort Key: a.orderid
Sort Method: quicksort Memory: 1695kB
-> Bitmap Heap Scan on orderitemattribute a (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
Recheck Cond: ((attributeid)::text = 'display-album'::text)
-> Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
Index Cond: ((attributeid)::text = 'display-album'::text)
-> Sort (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
Sort Key: b.orderid
Sort Method: quicksort Memory: 76kB
-> Bitmap Heap Scan on orderitem b (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
Recheck Cond: ((productid)::text = 'ModernBook'::text)
-> Bitmap Index Scan on id_orderitem_productid (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
Index Cond: ((productid)::text = 'ModernBook'::text)
Total runtime: 842.134 ms
(17 rows)
Intente leerlo usted mismo y vea si tiene sentido.
Lo que leí es que la base de datos primero escanea el id_orderitem_productid
índice, usándolo para encontrar las filas de las que quiere orderitem
, luego clasifica ese conjunto de datos usando una clasificación rápida (la clasificación usada cambiará si los datos no caben en la RAM), luego lo deja a un lado.
A continuación, escanea orditematt_attributeid_idx
para encontrar las filas que busca orderitemattribute
y luego clasifica ese conjunto de datos usando una clasificación rápida.
Luego, toma los dos conjuntos de datos y los fusiona. (Una combinación de combinación es una especie de operación de "comprimir" en la que recorre los dos conjuntos de datos ordenados en paralelo y emite la fila unida cuando coinciden).
Como dije, trabajas desde la parte interior del plan hasta la parte exterior, de abajo hacia arriba.
También hay una herramienta de ayuda en línea disponible, Depesz , que resaltará dónde están las partes costosas de los resultados del análisis.
también tiene uno, aquí están los mismos resultados , que para mí aclaran dónde está el problema.
PgAdmin le mostrará una representación gráfica del plan de explicación. Alternar entre los dos puede realmente ayudarlo a comprender lo que significa la representación del texto. Sin embargo, si solo desea saber qué va a hacer, es posible que pueda usar siempre la GUI.
La documentación oficial de PostgreSQL proporciona una explicación interesante y detallada sobre cómo comprender la salida de la explicación.