Poda de Particiones
Demuestra con EXPLAIN que las particiones se descartan de los planes; de lo contrario, la partición solo añade sobrecarga DDL sin reducción de escaneo.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
-- Amigable para poda: literal o parámetro enlazado a la clave de partición
EXPLAIN (COSTS OFF)
SELECT * FROM events
WHERE occurred_at >= TIMESTAMPTZ '2026-03-01'
AND occurred_at < TIMESTAMPTZ '2026-04-01';
-- Habilita la poda de particiones (activado por defecto en PG 18)
SET enable_partition_pruning = on;
SET enable_partitionwise_join = off; -- prueba primero la línea baseCuándo usar esto:
- Después de crear tablas particionadas - verifica antes del tráfico de producción.
- Cuando la latencia de la consulta coincide con un escaneo de tabla completa a pesar de la partición.
- Cuando el SQL generado por el ORM envuelve la clave de partición en funciones.
Ejemplo de Trabajo
CREATE TABLE metrics (
device_id text NOT NULL,
recorded_at timestamptz NOT NULL,
cpu_pct numeric(5, 2) NOT NULL,
PRIMARY KEY (device_id, recorded_at)
) PARTITION BY RANGE (recorded_at);
CREATE TABLE metrics_2026_01 PARTITION OF metrics
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE metrics_2026_02 PARTITION OF metrics
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE metrics_2026_03 PARTITION OF metrics
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
INSERT INTO metrics SELECT 'd1', '2026-01-10', 12.5;
INSERT INTO metrics SELECT 'd1', '2026-02-10', 22.0;
INSERT INTO metrics SELECT 'd1', '2026-03-10', 33.0;
ANALYZE metrics;
EXPLAIN (COSTS OFF, VERBOSE)
SELECT AVG(cpu_pct) FROM metrics
WHERE recorded_at >= '2026-03-01' AND recorded_at < '2026-04-01';Características del plan esperadas:
- El nodo de escaneo hace referencia solo a
metrics_2026_03. - La salida de
EXPLAINincluyePartitions pruned: 2(PostgreSQL 14+). - No hay hijos
Appendpara los meses podados.
Lo que esto demuestra:
- El predicado de rango semiabierto alineado con los límites de la partición habilita la poda.
ANALYZEen la tabla padre actualiza las estadísticas de todas las particiones.VERBOSEmuestra qué relaciones hijas se escanean.
Análisis Profundo
Cómo Funciona
- Poda estática en el momento de la planificación cuando los predicados son constantes (
WHERE recorded_at = '2026-03-15'). - Poda en tiempo de ejecución cuando los predicados usan parámetros (
$1) - aún poda por ejecución en sentencias preparadas. - La poda requiere comparaciones en la expresión de la clave de partición, no en formas envueltas como
date_trunc('month', recorded_at). - La configuración
constraint_exclusiones obsoleta; la poda declarativa usaenable_partition_pruning.
Lista de Verificación de Poda
| Forma del predicado | ¿Poda? |
|---|---|
col >= '2026-03-01' AND col < '2026-04-01' | Sí (rango) |
col IN ('2026-03-05', '2026-03-06') | Sí |
date_trunc('day', col) = '2026-03-01' | No - función en la clave |
| Sin predicado en la clave de partición | No - escaneo completo de todos los hijos |
OR entre columnas no de partición | Quizás parcial |
Notas SQL
-- Compara podado vs no podado
EXPLAIN (ANALYZE, BUFFERS, SUMMARY)
SELECT COUNT(*) FROM metrics WHERE device_id = 'd1';
EXPLAIN (ANALYZE, BUFFERS, SUMMARY)
SELECT COUNT(*) FROM metrics
WHERE recorded_at >= '2026-03-01' AND recorded_at < '2026-04-01';Trampas Comunes
- Función en la clave de partición -
WHERE occurred_at::date = CURRENT_DATEpuede no podar. Solución: rango entimestamptzcrudo. - Partición por defecto absorbe todo - la poda aún escanea la partición por defecto cuando los límites son desconocidos. Solución: particiones explícitas, monitorizar el tamaño de la partición por defecto.
- Funciones estables vs inmutables -
occurred_at AT TIME ZONE 'UTC'puede bloquear la poda. Solución: almacenar en UTC, comparar directamente. - Join sin filtro de clave de partición - la tabla de hechos está particionada pero el join de dimensión oculta la clave. Solución: empujar el filtro
logged_atal lado de la tabla de hechos. - Miles de particiones - el tiempo de planificación aumenta incluso con la poda. Solución: menos particiones, pero más grandes.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| BRIN en columna de tiempo | Tabla única, escaneos secuenciales aceptables | Necesidad de desvincular retención |
| Índices parciales por mes (no particionado) | Tablas pequeñas | Muchos meses de datos |
| Enrutamiento de shard a nivel de aplicación | Conocer el tenant de antemano | SQL ad-hoc de herramientas de BI |
| Hypertables de Timescale | Extensión de series de tiempo aprobada | La organización restringe extensiones |
Preguntas Frecuentes
¿Cómo veo el recuento de particiones podadas?
PostgreSQL 14+ muestra Partitions pruned: N en EXPLAIN para tablas particionadas. Usa EXPLAIN VERBOSE para nombres de hijos.
¿Las sentencias preparadas podan?
Sí - la poda en tiempo de ejecución evalúa los parámetros por ejecución. Prueba con PREPARE / EXECUTE en psql.
¿Ayuda el join partitionwise?
enable_partitionwise_join permite que los joins ocurran por par de particiones. Útil cuando ambos lados se particionan con la misma clave; mide antes de habilitar en producción.
¿Puedo podar en particiones hash de tenant_id?
Sí, cuando está presente WHERE tenant_id = 42. Las consultas sin tenant_id escanean todas las particiones hash.
¿Qué pasa con las subconsultas?
La poda funciona cuando los predicados son demostrablemente constantes. Las subconsultas volátiles en la clave de partición pueden evitar la poda estática.
¿`LIMIT` sin filtro de tiempo poda?
No. Sin un predicado de clave de partición, el planificador debe considerar todos los hijos para una semántica LIMIT correcta, a menos que una restricción demuestre la imposibilidad.
¿Cómo rompen los ORM la poda?
Pueden convertir timestamps a cadenas o aplicar DATE(columna). Registra el SQL con log_statement en staging y corrige las formas de los predicados.
¿Debería particionar por UTC?
Siempre almacena timestamptz en UTC. Los límites de poda deben usar literales UTC consistentes con los valores almacenados.
¿Puedo probar la poda en CI?
Almacena la lista de hijos esperada de EXPLAIN en pruebas de regresión; falla la CI si aparece un nuevo hijo en el plan para una consulta acotada.
¿Qué pasa si EXPLAIN muestra Append en todos los hijos?
Predicado faltante o no podable - corrige la consulta primero antes de añadir más particiones.
Relacionado
- DDL de Particionamiento Declarativo - configuración de particiones
- Conceptos Básicos de Particionamiento - ejemplo de EXPLAIN
- Conceptos Básicos de EXPLAIN - lectura de árboles de planes
- Mejores Prácticas de Particionamiento - guía sobre el número de particiones
Versiones de Stack: 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+.