PostgreSQL
Estadísticas y selectividad
Estadísticas del planner, histogramas, valores frecuentes, correlación y extended statistics para estimar cardinalidad y selectividad.
- Última actualización
- Actualizada
- Nivel
- Profundización
PostgreSQL
Estadísticas del planner, histogramas, valores frecuentes, correlación y extended statistics para estimar cardinalidad y selectividad.
El planner decide con una muestra estadística, no leyendo cada fila. Cuando la distribución real, la correlación entre columnas o los parámetros no están representados, una consulta puede tener un plan técnicamente razonable para datos que no existen.
ANALYZE recopila estadísticas que permiten estimar:
cuántas filas cumplen un filtro
→ selectividad
cuántos grupos resultarán
→ ndistinct
qué join order y algoritmo convienen
→ cardinalidad intermediaLas estadísticas no aceleran por sí mismas la ejecución. Mejoran las decisiones del planner.
Para planificar:
SELECT *
FROM orders
WHERE tenant_id = 10
AND status = 'pending';PostgreSQL necesita saber si espera:
No puede ejecutar toda la query durante planning. Utiliza catálogos y muestras.
La selectividad es la fracción estimada que cumple un predicado.
0.0001 → 0.01 %
0.5 → 50 %Cardinalidad estimada:
filas de tabla × selectividadLos errores se propagan por joins, aggregates y sorts.
ANALYZE app.orders;o columnas concretas:
ANALYZE app.orders (tenant_id, status, created_at);ANALYZE toma una muestra y actualiza catálogos. Consume I/O/CPU, pero normalmente permite operación concurrente.
Autovacuum ejecuta autoanalyze según cambios desde la última recolección.
Vista accesible:
SELECT *
FROM pg_stats
WHERE schemaname = 'app'
AND tablename = 'orders';Campos importantes:
Fracción NULL.
Número estimado de valores distintos.
-1 sugiere casi todos distintos.Valores frecuentes y sus proporciones.
Rangos de valores no representados como MCV.
Relación entre orden del valor y orden físico de la tabla, de -1 a 1.
Pueden aparecer para arrays y tipos compatibles.
Si status='paid' representa 95 %, el MCV permite estimarlo mejor que asumir distribución uniforme.
Para valores fuera de MCV, PostgreSQL reparte la masa restante entre distintos estimados.
Un target pequeño puede no incluir valores raros importantes.
El histograma divide la distribución ordenable en buckets aproximadamente equipoblados.
Para:
WHERE created_at >= $1 AND created_at < $2el planner interpola qué parte del histograma cae en el rango.
Limitaciones:
Si created_at aumenta junto al orden físico, correlation cercana a 1 indica que un index scan por rango puede visitar heap de forma relativamente secuencial.
Si UUID aleatorio tiene correlation baja, los accesos pueden saltar más.
Correlation no es correlación entre dos columnas; es entre valor y ubicación física.
Disparo conceptual:
analyze_threshold
+ analyze_scale_factor × número de filasEn tablas enormes, un scale factor default puede requerir millones de cambios antes de analizar.
Configuración por tabla:
ALTER TABLE app.orders SET (
autovacuum_analyze_scale_factor = 0.01,
autovacuum_analyze_threshold = 1000
);No copies valores sin medir tasa de cambio y coste.
Después de:
puede ser necesario:
ANALYZE app.orders;Crear un índice no corrige estadísticas de columnas automáticamente en todos los aspectos del workload.
ALTER TABLE app.orders
ALTER COLUMN customer_id SET STATISTICS 500;Aumenta tamaño de muestra y número de entradas MCV/histogram.
Beneficios:
Costes:
Aumenta en columnas problemáticas, no globalmente sin evidencia.
Afecta columnas sin override. Cambiarlo globalmente puede aumentar mantenimiento en todas las tablas.
Primero identifica dónde existen misestimaciones.
Sin información adicional, el planner puede multiplicar selectividades:
P(tenant_id=10 AND status='pending')
≈ P(tenant_id=10) × P(status='pending')Pero quizá tenant 10 tiene 80 % pending y el resto 0.1 %. La independencia es falsa.
CREATE STATISTICS orders_tenant_status_stats
(dependencies, mcv, ndistinct)
ON tenant_id, status
FROM app.orders;
ANALYZE app.orders;Tipos:
Describe dependencias funcionales aproximadas entre columnas.
Mejora estimación de combinaciones distintas, útil en GROUP BY.
Captura combinaciones frecuentes entre columnas.
No crean un índice ni aceleran acceso. Mejoran estimates.
tenant_id=1 → status casi siempre paid
tenant_id=2 → status casi siempre pendingEstadísticas individuales conocen tenants y statuses, pero no la combinación. MCV multicolumna puede representarla.
Si zip_code determina casi siempre city, un filtro por ambos no debería multiplicar selectividades como si fueran independientes.
Las dependencies son estadísticas, no constraints. No protegen integridad.
Versiones modernas permiten estadísticas sobre expresiones en formas compatibles:
CREATE STATISTICS orders_day_stats
ON (date_trunc('day', created_at))
FROM orders;Verifica sintaxis y capacidades de la major version. Pueden mejorar estimates sin crear índice, pero no hacen sargable el filtro.
Un expression index recopila estadísticas sobre su expresión. Esto puede ayudar al planner, además de ofrecer acceso.
No crees índice solo para estadísticas si una statistics object más barata resuelve el estimate.
Cada partition tiene estadísticas propias. La tabla particionada puede tener estadísticas a nivel parent para ciertos planes.
Problemas:
Automatiza ANALYZE tras cargas.
Autovacuum no analiza temp tables de la misma forma porque son de sesión. Si cargas muchas filas:
ANALYZE temp_result;Sin estadísticas, joins posteriores pueden estimarse mal.
pg_class.reltuples contiene estimación de filas, actualizada por vacuum/analyze y escalada según páginas.
No es un COUNT exacto.
Usa para capacidad/diagnóstico aproximado; no para facturación o lógica.
Un prepared statement puede cambiar de custom a generic plan.
Custom:
tenant_id=small devuelve poco.Generic:
Síntoma:
mismo SQL
→ rápido para algunos parámetros
→ lento para otrosInvestiga plan_cache_mode, driver y distribución antes de forzar.
Síntomas:
Acción:
ANALYZE table;Si mejora temporalmente pero vuelve a fallar, ajusta autoanalyze o diseño.
Persisten errores por:
No repitas ANALYZE como ritual.
Set-returning functions pueden declarar estimaciones:
CREATE FUNCTION ...
RETURNS SETOF ...
ROWS 100
COST 10;Defaults incorrectos pueden distorsionar joins. Ajusta basado en comportamiento medido.
Consulta distribución real con muestras/aggregates seguros:
SELECT status, count(*)
FROM orders
GROUP BY status;Compara con pg_stats. En tablas enormes, evita consultas costosas en incidentes sin plan.
EXPLAIN muestra estimate; ANALYZE muestra actual.
Plan estima:
orders tenant=42, status=pending → 20 filasReal:
500 000 filasProceso:
pg_stats de tenant/status.n_distinct puede representarse negativo. Histogramas aportan poco para igualdad individual.
Consultas sobre tail reciente pueden caer fuera del histograma y estimarse mal.
MCV queda stale antes del threshold.
La muestra puede cubrirla completa; los planes pueden seguir prefiriendo seq scan correctamente.
Requiere ANALYZE/reindex según operación.
Estadísticas se replican físicamente, pero workload de lectura puede ser distinto; planes usan los mismos datos/configuración local.
Una describe distribución; la otra actividad.
Aumenta mantenimiento sin foco.
No crean access path.
Inaceptable; es mantenimiento.
Es estimación.
Genera estimates multiplicativos falsos.
pg_stats.n_distinct negativo?Vacuum, autovacuum y bloat explica cómo PostgreSQL mantiene el espacio y la visibilidad producidos por MVCC.