Conceptos básicos de EXPLAIN
EXPLAIN muestra la ruta de acceso elegida por el planificador antes de que incurras en el coste de ejecutar una consulta. EXPLAIN (ANALYZE, BUFFERS) ejecuta la consulta y añade tiempos y E/S de búfer para que puedas ver lo que realmente sucedió en PostgreSQL 18.4.
Receta
Tarjeta de referencia rápida - lista para copiar y pegar.
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, WAL)
SELECT o.id, o.total
FROM orders o
WHERE o.status = 'shipped'
AND o.created_at >= now() - interval '7 days';Cuándo usar esto: Cualquier consulta lenta o sorprendente en un conjunto de datos similar a producción. Captura el plan antes y después de cambios en índices o estadísticas.
Ejemplo de trabajo
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL,
status text NOT NULL DEFAULT 'pending',
created_at timestamptz NOT NULL DEFAULT now(),
total numeric(12,2) NOT NULL
);
CREATE INDEX orders_status_created_idx ON orders (status, created_at);
INSERT INTO orders (customer_id, status, created_at, total)
SELECT
(random() * 10000)::bigint,
CASE WHEN random() < 0.05 THEN 'shipped' ELSE 'pending' END,
now() - (random() * interval '90 days'),
(random() * 500)::numeric(12,2)
FROM generate_series(1, 200000);
ANALYZE orders;
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT id, total
FROM orders
WHERE status = 'shipped'
AND created_at >= now() - interval '7 days';Un plan saludable a menudo muestra Index Scan en orders_status_created_idx con bajos rows removed by filter y modestos recuentos de shared hit.
Lo que esto demuestra:
ANALYZEantes de comparar planes- Lectura del tipo de escaneo y nombre del índice
- Interpretación de
actual timeyrowsfrente a las estimaciones del planificador - Uso de
BUFFERSpara detectar fallos en la caché
Profundización
Cómo funciona
- El planificador construye un árbol de nodos (escaneo, unión, ordenación, agregación). Cada nodo tiene recuentos de filas estimados y reales.
EXPLAINpor sí solo es barato y seguro en producción.ANALYZEejecuta la consulta, así que úsalo en staging o con salvaguardasLIMITen sentencias destructivas.BUFFERSinformashared hit(caché) yshared read(disco). Un altoreaden una consulta frecuente significa que más RAM o mejores índices pueden ayudar.SETTINGS(PostgreSQL 12+) muestra GUCs no predeterminados que influyeron en el plan, útil cuandorandom_page_costowork_memdifieren por rol.
Opciones de EXPLAIN de un vistazo
| Opción | Propósito |
|---|---|
ANALYZE | Ejecutar consulta; mostrar tiempos reales |
BUFFERS | Hits/lecturas de búfer por nodo |
VERBOSE | Mostrar listas de columnas y nombres de esquema |
SETTINGS | Mostrar valores GUC que afectan al planificador |
WAL | Bytes WAL generados (escrituras) |
FORMAT JSON | Plan legible por máquina para herramientas |
Notas SQL
-- Guardar un plan para un ticket (sin ejecución)
EXPLAIN (FORMAT JSON)
SELECT count(*) FROM orders WHERE status = 'pending';
-- Comparar solo estimaciones (seguro en producción)
EXPLAIN (VERBOSE)
SELECT * FROM orders WHERE customer_id = 42;Errores comunes
- Ejecutar
EXPLAIN ANALYZEenDELETE/UPDATEsin cuidado - Modificas datos de producción. Solución: Envuelve en una transacción yROLLBACK, o prueba en una instantánea. - Confiar en números de caché fría una vez - La primera ejecución después de un reinicio parece peor que el estado estable. Solución: Ejecuta dos veces; compara el segundo resultado de
ANALYZE. - Ignorar la deriva de la estimación de filas -
rows=1000vsactual rows=85000indica estadísticas incorrectas o predicados correlacionados. Solución:ANALYZEy comprueba estadísticas extendidas. - Leer solo el tiempo total - Un nodo externo barato puede ocultar un costoso bucle anidado en su interior. Solución: Encuentra el nodo con la mayor parte del
actual time. - Olvidar
search_path- Los planes difieren cuando la cualificación del esquema cambia. Solución: Establecesearch_pathexplícitamente en la sesión que captures.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
auto_explain | Captura continua de consultas lentas | Necesitas iteración interactiva |
pg_stat_statements | Coste agregado por texto de consulta | Necesitas detalle de plan único |
EXPLAIN (GENERIC_PLAN) | Planificación de sentencias preparadas | Las estadísticas de tablas están muy desactualizadas |
Preguntas frecuentes
¿Es EXPLAIN seguro en producción?
EXPLAIN sin ANALYZE es planificación de solo lectura. EXPLAIN ANALYZE ejecuta la consulta; ten cuidado con las escrituras y los escaneos pesados.
¿Qué significa "Seq Scan"?
PostgreSQL leyó la tabla de datos secuencialmente. A menudo es correcto para grandes fracciones de la tabla o tablas muy pequeñas.
¿Por qué difieren las filas estimadas y reales?
Estadísticas desactualizadas, columnas correlacionadas o datos sesgados. Ejecuta ANALYZE y considera estadísticas extendidas.
¿Debo usar el formato TEXT o JSON?
TEXT para humanos en tickets. JSON para explain.dalibo.com, pganalyze, o herramientas de comparación de planes en CI.
¿EXPLAIN muestra esperas de bloqueo?
No. Comprueba pg_stat_activity y wait_event_type para la contención de bloqueos separada de la forma del plan.
¿Qué son "Buffers: shared hit=..."?
Páginas encontradas en los búferes compartidos de PostgreSQL (caché) frente a las leídas del SO/disco.
¿Puedo hacer EXPLAIN a una sentencia preparada?
Sí: EXPLAIN EXECUTE stmt_name(args) o EXPLAIN (GENERIC_PLAN) EXECUTE ... para planes genéricos.
¿Cómo oculto literales sensibles?
Usa marcadores de posición de parámetros en las consultas de la aplicación; evita pegar PII en registros de planes compartidos.
¿Cambia PostgreSQL 18 la salida de EXPLAIN?
El formato del árbol principal es estable; pueden aparecer nuevos nodos del planificador. Siempre fija la versión en las líneas base de regresión.
¿Qué debo leer a continuación?
Páginas sobre las compensaciones entre escaneo secuencial e indexado y algoritmos de unión en esta sección.
Relacionado
- Seq Scan vs Index Scan - Cuándo gana cada escaneo
- Nested Loop, Hash Join, Merge Join - Nodos de unión en planes
- Estudios de caso de planes incorrectos - Patrones reales de estimaciones erróneas
- Conceptos básicos de estadísticas - Por qué las estimaciones salen mal
Versiones de pila: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x y PostGIS 3.5+.