Bucle Anidado, Unión Hash, Unión por Fusión
Los nodos de unión combinan conjuntos de filas de dos entradas. PostgreSQL 18.4 elige entre bucle anidado, hash o unión por fusión basándose en los recuentos de filas, la memoria disponible, las oportunidades de índice y el orden de clasificación.
Receta
Tarjeta de referencia rápida - lista para copiar y pegar.
EXPLAIN (ANALYZE, BUFFERS)
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= now() - interval '1 day';Cuándo usar esto: Las consultas con muchas uniones disparan la CPU o la E/S. Identifica qué algoritmo de unión domina el plan.
Ejemplo de Trabajo
CREATE TABLE customers (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers(id),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX orders_customer_id_idx ON orders (customer_id);
CREATE INDEX orders_created_at_idx ON orders (created_at);
INSERT INTO customers (name)
SELECT 'customer-' || i FROM generate_series(1, 50000) AS i;
INSERT INTO orders (customer_id, created_at)
SELECT (random() * 49999 + 1)::bigint, now() - (random() * interval '30 days')
FROM generate_series(1, 500000);
ANALYZE customers;
ANALYZE orders;
EXPLAIN (ANALYZE, BUFFERS)
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= now() - interval '1 day';Lo que esto demuestra:
- Bucle anidado con índice en el lado interno para conjuntos externos pequeños
- Unión hash cuando construir una tabla hash es más barato que las sondas de índice repetidas
- Cómo los filtros selectivos reducen la entrada externa antes de la unión
Análisis Profundo
Cómo Funciona
- Bucle Anidado - Por cada fila externa, escanea la entrada interna (a menudo respaldada por un índice). Gana cuando la entrada externa es diminuta o la interna tiene un índice selectivo.
- Unión Hash - Construye una tabla hash en la entrada más pequeña, sondea con la más grande. Necesita
work_mem; se desborda a disco si se excede (Hash Buckets/Batchesen el plan). - Unión por Fusión - Ambas entradas ordenadas por las claves de unión; se fusionan como en la ordenación por fusión. Necesita entradas pre-ordenadas o ordenaciones explícitas; excelente para uniones equi-join grandes en datos ordenados.
Señales de Selección de Algoritmo
| Algoritmo | Condiciones favorables |
|---|---|
| Bucle Anidado | Cardinalidad externa pequeña; índice en la clave de unión interna |
| Unión Hash | Conjuntos medianos/grandes; equi-join; suficiente work_mem |
| Unión por Fusión | Entradas ya ordenadas; uniones secuenciales grandes |
Notas SQL
-- Revela el orden y tipo de unión
EXPLAIN (VERBOSE, ANALYZE)
SELECT count(*)
FROM orders o
JOIN customers c ON c.id = o.customer_id;
-- Indicador de desbordamiento hash
-- Busca: "Buckets: ... Batches: 2" en el nodo Hash Join
SET work_mem = '64MB';Trampas
- Bucle anidado con escaneo secuencial interno - Desastre a escala: O(n*m). Solución: Indexa la clave de unión interna o reescribe a unión hash mediante mejores estadísticas.
- Desbordamiento a disco de la unión hash -
Batches > 1significa quework_memes demasiado bajo para ese nodo. Solución: Aumentawork_memsolo para el rol que informa, o reduce las filas antes con filtros. - Producto cartesiano disfrazado de bucle anidado - Condición de unión faltante. Solución: Verifica las cláusulas
ON/USINGen el SQL generado por el ORM. - Unión por fusión con doble ordenación - Dos ordenaciones costosas cuando ninguna entrada está ordenada. Solución: Indexa las columnas principales para que coincidan con las claves de unión y
ORDER BY. - Estadísticas obsoletas que cambian el tipo de unión - El plan cambia después de
ANALYZEsin cambios en el código. Solución: Captura planes en CI; alerta sobre regresiones.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Desnormalizar claves de unión "calientes" | Segmento de panel de lectura intensiva | OLTP normalizado con escritura intensiva |
| Vista materializada | Unión costosa reutilizada cada hora | Se requiere consistencia en tiempo real |
| Subconsulta LATERAL | Patrón Top-N por grupo | Unión equi-join simple de dos tablas grandes |
Preguntas Frecuentes
¿Cuál unión es la más rápida?
Depende de la cardinalidad y los índices. No hay un ganador universal; lee el plan para el tamaño de tus datos.
¿Qué es un nodo Memoize?
PostgreSQL 14+ almacena en caché los resultados internos del bucle anidado para claves externas repetidas. Genial para bucles anidados con parámetros internos estables.
¿Puedo forzar una unión hash?
SET enable_nestloop = off solo en una sesión de prueba. Arregla las estadísticas y los índices en su lugar para producción.
¿Por qué unión por fusión en tablas no ordenadas?
El planificador añade nodos Sort en ambas entradas. Comprueba el coste de la ordenación; un índice puede eliminar las ordenaciones.
¿Importa el orden de la unión?
Sí. PostgreSQL considera la reordenación a través del optimizador genético en muchas tablas. Las malas estimaciones producen un mal orden.
¿Planes de semi-unión y anti-unión?
EXISTS, IN y NOT EXISTS a menudo se convierten en nodos hash semi-join o bucle anidado semi-join.
¿Unión hash paralela?
Las construcciones grandes pueden usar Unión Hash Paralela en PostgreSQL 18. Requiere max_parallel_workers_per_gather > 0 y entradas grandes.
¿Cómo ayudan las claves foráneas?
No indexan automáticamente. Todavía necesitas índices en las columnas de unión secundarias para el rendimiento del bucle anidado.
¿Qué es una señal de advertencia de producto cartesiano?
Bucle anidado con escaneo secuencial interno y recuentos de filas que se multiplican hacia el cuadrado del tamaño del resultado.
¿Qué debo leer a continuación?
Página de agregaciones de ordenación y hash para patrones de desbordamiento de memoria compartidos con uniones hash.
Relacionado
- Agregaciones de Ordenación y Hash - Desbordamientos de
work_mem - Estudios de Caso de Planes Malos - Fallos en el orden de unión
- Conceptos Básicos de Diseño de Índices - Índices de columnas de unión
- Ajuste de work_mem - Memoria por nodo para ordenación/hash
Versiones de la 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+.